Ten ebook jest chroniony znakiem wodnym

helion.pl Kopia dla:

5561683950 Pietrzak Piotr Incept piotr.1969@gmail.com
piotr.1969@gmail.com

Unmesh Gundecha

Selenium i testowanie aplikacji

Receptury

Wydanie II

Tytuł oryginału: Selenium Testing Tools Cookbook, Second Edition

Tłumaczenie: Radosław Meryk

ISBN: 978-83-283-3412-0

Copyright © Packt Publishing 2015

First published in the English language under the title 'Selenium Testing Tools Cookbook - Second Edition - (9781784392512)'

Polish edition copyright © 2017 by Helion SA

All rights reserved.

All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher.

Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji.

Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli.

Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce.

Wydawnictwo HELION

ul. Kościuszki 1c, 44-100 GLIWICE

tel. 32 231 22 19, 32 230 98 63

e-mail: helion@helion.pl

WWW: http://helion.pl (księgarnia internetowa, katalog książek)

Drogi Czytelniku!

Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres

http://helion.pl/user/opinie/seler2_ebook

Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.

Poleć książkę

Kup w wersji papierowej

Oceń książkę

Księgarnia internetowa

Lubię to! » nasza społeczność

Zespół wydania oryginalnego

Author

Unmesh Gundecha

Reviewers

Alexander Afanasyev

Anuj Chaudhary

Oliver Gondža

Vatsala Dorairajan

Acquisition Editor

Tushar Gupta

Content Development Editor

Kirti Patil

Technical Editor

Rupali R. Shrawane

Copy Editor

Vatsal Surti

Project Coordinator

Nidhi Joshi

Proofreader

Safis Editing

Indexer

Tejal Daruwale Soni

Graphics

Jason Monteiro

Production Coordinator

Manu Joseph

Cover Work

Manu Joseph

O autorze

Unmesh Gundecha posiada tytuł magistra w dziedzinie inżynierii oprogramowania i ma ponad trzynastoletnie doświadczenie w rozwoju i testowaniu oprogramowania. Pracował jako architekt w projektach automatyzacji testów funkcjonalnych przy użyciu standardowych i niestandardowych frameworków automatyzacji testów w połączeniu z wiodącymi narzędziami do automatyzacji testów — zarówno komercyjnymi, jak i open source. Obecnie pracuje jako architekt testów w międzynarodowej firmie w Pune w Indiach. Unmesh jest również autorem wydanej przez Packt Publishing książki Learning Selenium Testing Tools with Python.

O recenzentach

Alexander Afanasyev jest obecnie programistą Pythona, a także architektem i liderem automatyzacji QA w firmie Avenues International Inc zajmującej się usługami doradczymi w zakresie analizy danych. Uzyskał tytuł magistra w dziedzinie inżynierii elektronicznej. Posiada kilkuletnie doświadczenie w rozwoju i testowaniu oprogramowania w wielu firmach z różnych sektorów, zarówno w Rosji, jak i Stanach Zjednoczonych. Pasją Alexandera zawsze było testowanie i dbanie o jakość kodu oraz technologii webowych. W wolnym czasie udziela się w serwisach StackOverflow i GitHub, czyta książki lub gra na gitarze.

Chciałbym podziękować Danielowi Lyakovetsky’emu za „otwarcie drzwi”, wskazówki i zaufanie, jakim mnie obdarzył. Ogromne podziękowania należą się mojej wspaniałej żonie Annie i mojej córeczce Kate; one są moim stałym źródłem wsparcia.

Anuj Chaudhary jest inżynierem oprogramowania, który lubi pracę nad testowaniem oprogramowania i automatyzacją tego procesu. Ma olbrzymie doświadczenie w różnych metodologiach testowania, takich jak testowanie ręczne, automatyczne czy wydajności i bezpieczeństwa. Pracował jako indywidualny kontrybutor oraz techniczny lider w różnych projektach oprogramowania oraz uczestniczył we wszystkich etapach cyklu życia aplikacji.

Anujowi dwa razy z rzędu przyznano tytuł Microsoft MVP. Prowadzi blogi pod adresem http://www.anujchaudhary.com/. Jest korektorem następujących książek poświęconych Selenium:

Selenium WebDriver Practical Guide (https://www.packtpub.com/web-development/selenium-webdriver-practical-guide),

Selenium Design Patterns and Best Practices (https://www.packtpub.com/web-development/selenium-design-patterns-and-best-practices),

Mastering Selenium WebDriver (https://www.packtpub.com/web-development/mastering-selenium-webdriver).

Chciałbym podziękować mojej żonie Renu i synowi Arjunowi za wsparcie, które od nich otrzymałem, oraz za to, że pozwolili mi poświęcić dodatkowy czas na korektę tej książki.

Oliver Gondža jest entuzjastą Javy, programistą ekstremalnym, kontrybutorem OSS oraz zwolennikiem systemu Red Hat.

Vatsala Dorairajan jest początkującym technologiem oprogramowania. Pracowała głównie w projektach koncepcyjnych. Zajmowała się przekształcaniem pomysłów zaprezentowanych na papierze w działające prototypy, które z kolei ewoluowały w produkty. Dotychczas jej doświadczenie techniczne obejmuje takie technologie jak Java, Flex, Python, PHP i Ruby on Rails. Obecnie pracuje w Jombay, wiodącej firmie zajmującej się pomiarami i analizą talentów, która pomaga firmom zewnętrznym wynajmować, promować i utrzymywać odpowiednie kadry.

Vatsala była również korektorką wydanej przez Packt Publishing książki Selenium Testing Tools Starter.

Przedmowa

Selenium to zestaw narzędzi umożliwiających automatyzację przeglądarek. Jest używany przede wszystkim do testowania aplikacji, ale jego zastosowania nie ograniczają się do tego rodzaju zadań. Może również służyć do realizacji techniki screen scraping (dosł. zdrapywania z ekranu) oraz automatyzacji powtarzających się zadań w oknie przeglądarki. Selenium obsługuje automatyzację wszystkich głównych przeglądarek, w tym Firefox, Internet Explorer, Google Chrome, Safari i Opera. Selenium WebDriver jest obecnie częścią standardów W3C, jest też obsługiwany przez główne przeglądarki.

Książka pomoże Ci nauczyć się zaawansowanych technik testowania aplikacji webowych z zastosowaniem Selenium WebDriver API i pokrewnych narzędzi. Dowiesz się z niej, jak — korzystając z Selenium WebDriver — skutecznie i efektywnie przetestować aplikacje webowe dla komputerów stacjonarnych, przeglądarek mobilnych i środowisk rozproszonych.

Autor w książce opisuje podstawowe funkcje Selenium WebDriver, a także wzorce projektowe, takie jak testy bazujące na danych (ang. data-driven testing), obiekty stron (ang. page objects) i mapy obiektów. Ich stosowanie pozwala na projektowania łatwego do utrzymania i niezawodnego frameworka automatyzacji testów. Podczas lektury dowiesz się również, jak zintegrować Selenium WebDriver z ATDD/BDD, narzędziami budowania i ciągłej integracji, a także w jaki sposób przeprowadzić testowanie webowych aplikacji mobilnych za pomocą Appium.

W książce opisano techniki rozszerzania frameworka Selenium dla specyficznych potrzeb. Zaprezentowano również ponad 80 receptur, które można wykorzystać do zbudowania lub rozszerzenia istniejącego frameworka automatyzacji testów.

Co zawiera książka?

Rozdział 1., „Od czego zacząć?”, to opis, jak skonfigurować Selenium WebDriver z narzędziami Eclipse, Maven lub ANT w celu rozwijania testów na platformie Javy. Następnie omówione zostało konfigurowanie Visual Studio, Ruby i Pythona do rozwoju testów. W tym rozdziale pokazano również sposób konfiguracji różnych przeglądarek w celu testowania.

Rozdział 2., „Wyszukiwanie elementów”, zawiera wprowadzenie do technik lokalizatora obsługiwanych przez Selenium WebDriver. Techniki te pozwalają znaleźć elementy na stronach w aplikacjach webowych. Selenium WebDriver dostarcza szereg technik znajdowania elementów na stronach WWW. Pozwala na stosowanie różnych strategii lokalizacji, takich jak XPath, CSS, i DOM. Można także zaimplementować własne strategie lokalizowania elementów. Rozdział pomoże Ci również rozpocząć pracę z API lokalizatora Selenium WebDriver.

Rozdział 3., „Interakcje z elementami”, to wyjaśnienie, jak używać Selenium WebDriver API do zautomatyzowania interakcji z różnymi typami elementów interfejsu użytkownika w aplikacji webowej, w tym polami tekstowymi, przyciskami, rozwijanymi listami, przełącznikami, polami wyboru i tabelami.

Rozdział 4., „Korzystanie z Selenium API”, to pokazanie, jak używać Selenium WebDriver API do tworzenia testów. Zaprezentowano w nim interfejs API oraz zbadano zaawansowane interakcje z użytkownikiem w celu wykonywania złożonych operacji za pomocą myszy i klawiatury, a także do pracy z różnymi rodzajami elementów interfejsu użytkownika w aplikacjach webowych.

Rozdział 5., „Synchronizacja testów”, stanowi wyjaśnienie, jak użyć Selenium WebDriver API do obsługi synchronizacji z niejawnym i jawnym oczekiwaniem w celu implementacji solidnych i niezawodnych testów.

Rozdział 6., „Alerty, ramki i okienka”, zawiera opis obsługi wielu okien (w tym okien popup) oraz sposobu postępowania z alertami, które wyświetlają się podczas wykonywania testu.

Rozdział 7., „Testowanie sterowane danymi”, przedstawia powszechnie stosowaną metodologię w automatyzacji testów — testowanie sterowane danymi (ang. data driven testing). Selenium WebDriver nie posiada wbudowanych funkcji do obsługi testów bazujących na danych. Można jednak rozszerzyć Selenium WebDriver API do obsługi tej techniki. W tym rozdziale zawarto receptury obsługi technik testowania bazujących na danych (z odczytem danych z arkuszy kalkulacyjnych) z wykorzystaniem frameworków JUnit, TestNG i Apache POI.

Rozdział 8., „Wzorzec Page Object”, to wprowadzenie wzorca Page Object, który jest szeroko stosowany do tworzenia struktury testów bazujących na Selenium WebDriver. W tym rozdziale zamieszczono przykłady i wskazówki dotyczące sposobu tworzenia frameworków testowania przy użyciu wzorca Page Object.

Rozdział 9., „Rozszerzanie Selenium”, zawiera objaśnienie, jak rozszerzyć Selenium WebDriver API poprzez dodanie funkcji do tworzenia skalowalnego frameworka automatyzacji testów. W tym rozdziale opisano niektóre ważne receptury rozszerzania Selenium WebDriver w celu realizacji różnych praktycznych scenariuszy, takich jak obsługa niestandardowych kontrolek interfejsu użytkownika, przechwytywanie obrazów elementów oraz wykonywanie weryfikacji na podstawie obrazu.

Rozdział 10., „Testowanie aplikacji webowych HTML5”, to prezentacja użycia Selenium WebDriver do testowania aplikacji webowych korzystających ze standardu HTML5. W tym rozdziale wyjaśniono, jak testować elementy video i canvas oraz API Web Storage standardu HTML5.

Rozdział 11., „Behavior-Driven Development”, zawiera wprowadzenie do korzystania z technik BDD za pośrednictwem Selenium WebDriver oraz takich narzędzi jak Cucumber-JVM, SpecFlow.NET, Behave dla Pythona oraz Capybara dla Ruby.

Rozdział 12., „Integracja z innymi narzędziami”, pokazuje, jak skonfigurować WebDriver z systemem Jenkins w celu uruchomienia testów z wykorzystaniem technik ciągłej integracji oraz narzędzi Maven i ANR. W tym rozdziale opisano również receptury użycia takich narzędzi jak AutoIt i Sikuli do testowania niewebowych interfejsów użytkownika.

Rozdział 13., „Testowanie w wielu przeglądarkach”, demonstruje, w jaki sposób skonfigurować rozproszone środowisko testów oraz funkcjonalność Selenium Grid do testowania różnych przeglądarek. Pokazano tu sposób dodawania węzłów z różnymi kombinacjami przeglądarki i systemu operacyjnego. Uruchomiono równoległe testy przy użyciu narzędzia TestNG, które pozwala skrócić czas wykonywania testów i zwiększa zakres ich pokrycia. W rozdziale opisano również sposób wykorzystania usług w chmurze, takich jak Sauce Labs i BrowserStack, w celu testowania wielu przeglądarek.

Rozdział 14., „Testowanie aplikacji w przeglądarkach mobilnych”, zawiera wprowadzenie do testowania webowych aplikacji mobilnych z systemem Apple iOS oraz platform z systemem Android za pomocą narzędzia Appium. W tym rozdziale zawarto receptury konfigurowania Selenium WebDriver i korzystania z niego do testowania mobilnych aplikacji webowych w iPhone’ach oraz urządzeniach (symulatorach) z systemem Android.

Co jest potrzebne podczas lektury tej książki?

Do śledzenia receptur zamieszczonych w tej książce potrzebne będzie opisane niżej oprogramowanie.

Przeglądarki: Mozilla Firefox, Google Chrome lub Microsoft Internet Explorer.

Sterowniki przeglądarki Selenium: Chrome Driver i InternetExplorerDriver.

Narzędzia Selenium: sterownik klienta Selenium WebDriver (zgodny z preferencjami w zakresie języka programowania) oraz Selenium Standalone Server.

IDE: Eclipse, IntelliJ IDEA i Microsoft Visual Studio (dla platformy .NET).

Narzędzia frameworka BDD: Cucumber JVM, SpecFlow.NET (dla platformy .NET) oraz Capybara (dla Ruby).

Narzędzia budowania i integracji: Maven, ANT i Jenkins.

Inne narzędzia automatyzacji GUI: AutoIt i Sikuli.

Narzędzia mobilne: Apple Xcode (do testowania przeglądarek mobilnych w systemie iOS), Android SDK, Android, Android Server APK i Appium.

Środowiska runtime języków programowania: JDK 1.7 lub JDK 1.8 (dla Javy), Ruby 1.9 + (dla Ruby) i Python 2.7 + (dla Pythona).

Dla kogo jest ta książka

Książka jest przeznaczona dla specjalistów zapewnienia jakości (testerów), menedżerów testowania i programistów z wcześniejszymi doświadczeniami w używaniu narzędzi Selenium i języka Java do testowania aplikacji webowych.

Punkty

W książce występuje kilka często pojawiających się nagłówków („Przygotuj się”, „Jak to zrobić?”, „Jak to działa?”, „Co dalej?” i „Zobacz też”).

W celu przekazania wyraźnych instrukcji dotyczących sposobu realizacji receptury używamy wymienionych punktów w następujący sposób.

Przygotuj się

W tym punkcie dowiesz się, czego należy się spodziewać w recepturze. Znajdziesz tam opis sposobu konfigurowania dowolnego oprogramowania lub wszelkie ustawienia wstępne wymagane dla wybranej receptury.

Jak to zrobić?

Ten punkt zawiera czynności wymagane do realizacji receptury.

Jak to działa?

Tu zazwyczaj zawarte są szczegółowe objaśnienia tego, co wydarzyło się w poprzednim punkcie.

Co dalej?

W punkcie zamieszczono dodatkowe informacje dotyczące receptury, a wymagane do dostarczenia Ci bardziej obszernej wiedzy na temat tejże receptury.

Zobacz też

Ten punkt zawiera odsyłacze do innych przydatnych informacji związanych z recepturą.

Konwencje

W książce zastosowano różne style tekstu, które wyróżniają odmienne rodzaje informacji. Oto kilka przykładów tych stylów wraz z wyjaśnieniem ich znaczenia.

Słowa kluczowe w tekście, nazwy tabel baz danych, nazwy folderów, nazwy plików, rozszerzenia plików, nazwy ścieżek, atrapy adresów URL, dane wejściowe wprowadzane przez użytkownika oraz uchwyty do zasobów na Twitterze są wyświetlane w następujący sposób: „W obszarze edytora otworzy się plik pom.xml”.

Bloki kodu są zapisywane w następujący sposób.

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

Gdy chcemy zwrócić uwagę na określoną część bloku kodu, to wiersze, które chcemy wyróżnić, są zaznaczone pogrubieniem.

<artifactId>SeleniumCookbook</artifactId>

<version>0.0.1-SNAPSHOT</version>

<dependencies>

    <dependency>

        <groupId>org.seleniumhq.selenium</groupId>

Dane wprowadzane w wierszu polecenia lub wyjście wiersza polecenia są oznaczane w następujący sposób.

gem install selenium-webdriver

Nowe pojęcia i ważne słowa są pogrubione.

Słowa, które są wyświetlane na ekranie, na przykład w menu lub w oknach dialogowych, pojawiają się w tekście tak: „Zaznacz pole wyboru Create a simple project (skip archetype selection)”.

Ostrzeżenia, ważne informacje oraz porady i sztuczki pojawiają się w takich ramkach.

Opinie czytelników

Opinie naszych czytelników zawsze są mile widziane. Napisz, co myślisz o tej książce — co Ci się podobało lub nie podobało. Nadesłane opinie są dla nas ważne, ponieważ pomagają tworzyć książki, z których czytelnicy mogą maksymalnie skorzystać.

Obsługa klienta

Teraz, kiedy jesteś dumnym właścicielem książki wydanej przez Helion, zapoznaj się z kilkoma informacjami, które pozwolą Ci jak najlepiej skorzystać z tego zakupu.

Pobieranie przykładowego kodu

Przykładowy kod dla tej książki możesz pobrać pod adresem ftp://ftp.helion.pl/przyklady/seler2.zip.

Pobieranie kolorowych ilustracji do tej książki

Czytelnikom oferujemy również zip zawierający kolorowe ilustracje zrzutów ekranów (wykresów) wykorzystanych w tej książce. Kolorowe ilustracje pomogą Ci lepiej zrozumieć zmiany w danych wyjściowych. Można je pobrać pod adresem: ftp://ftp.helion.pl/przyklady/seler2.zip.

Errata

Mimo że podjęliśmy wszelkie starania, aby zapewnić dokładność publikowanej zawartości, pomyłki się zdarzają. Jeśli znajdziesz błąd w jednej z naszych książek — na przykład pomyłkę w tekście lub kodzie — będziemy wdzięczni, jeśli nas o tym poinformujesz. W ten sposób pomożesz innym czytelnikom. Zaoszczędzisz im frustracji, a nam pomożesz udoskonalić kolejne wydania tej książki. Jeżeli znajdziesz jakieś błędy drukarskie, zgłoś je, odwiedzając witrynę http://helion.pl/erraty.htm. Wybierz Twoją książkę i wprowadź szczegóły znalezionych błędów. Po zweryfikowaniu błędu Twoje zgłoszenie zostanie przyjęte i errata przesłana do naszej witryny internetowej lub dodana do dowolnej listy istniejących błędów w sekcji Errata dla tego tytułu.

Aby wyświetlić wcześniej dodane błędy, przejdź pod adres http://helion.pl/ksiazki/seler2.htm — w sekcji Errata wyświetlą się potrzebne informacje.

Rozdział 1.
Od czego zacząć?

W tym rozdziale zobaczysz, jak skonfigurować środowisko programistyczne tworzenia testów Selenium WebDriver. Zaprezentowałem tu kilka podstawowych ustawień, które pomogą w rozpoczęciu korzystania z Selenium WebDriver. Oto czynności, jakie nauczysz się wykonywać.

Konfigurowanie środowiska tworzenia testów Selenium WebDriver dla języka Java z Eclipse i Maven.

Korzystanie z systemu Ant do egzekucji testów Selenium WebDriver.

Konfigurowanie środowiska Microsoft Visual Studio dla tworzenia testów Selenium WebDriver.

Konfigurowanie frameworka Selenium WebDriver dla języków Python i Ruby.

Konfigurowanie Internet Explorer Driver Server.

Konfigurowanie sterownika ChromeDriver dla przeglądarki Google Chrome.

Konfigurowanie sterownika Microsoft WebDriver dla przeglądarki Microsoft Edge.

Wprowadzenie

Selenium WebDriver to powszechnie stosowany framework automatyzacji przeglądarek internetowych, używany w połączeniu z różnymi narzędziami, który swoją popularność zawdzięcza schludnemu i czystemu projektowi obiektowemu. Selenium WebDriver można zintegrować z innymi narzędziami w celu opracowania automatycznych testów.

W pierwszych punktach tego rozdziału opisałem integrację Selenium WebDriver z narzędziami wytwarzania oprogramowania i budowania aplikacji, takimi jak Eclipse, Maven i Microsoft Visual Studio. Narzędzia te zapewniają prosty sposób rozwijania frameworków automatyzacji testów i pozwalają rozszerzyć możliwości Selenium WebDriver API. Poniższe receptury wyjaśniają sposób zainstalowania i skonfigurowania tych narzędzi razem z Selenium.

Na koniec pokażę, jak skonfigurować sterowniki różnych przeglądarek i wprowadzić ustawienia początkowe dla systemu WebDriver.

Konfigurowanie środowiska tworzenia testów Selenium WebDriver dla języka Java z Eclipse i Maven

Selenium WebDriver to prosty interfejs API, który może pomóc w automatyzacji przeglądarek. Kiedy jednak jest używany do testowania i budowania frameworków testowych, wymagania rosną. Do opracowania nowego projektu Javy potrzebne jest zintegrowane środowisko tworzenia oprogramowania (IDE) lub edytor kodu. Aby zbudować framework testowania, trzeba jeszcze dodać Selenium WebDriver oraz inne zależności.

W świecie Javy powszechnie używanym środowiskiem IDE jest Eclipse, a także IntelliJ IDEA oraz NetBeans. Eclipse zapewnia bogate środowisko dla rozwijania testów bazujących na Selenium WebDriver.

Oprócz Eclipse potrzebny jest system Apache Maven, który obsługuje zarządzanie całym cyklem życia projektu testowego. Maven pozwala na zdefiniowanie struktury projektu, jego współzależności oraz umożliwia zarządzanie budowaniem i testowaniem aplikacji.

Eclipse i Maven mogą być używane do budowania frameworka testowego Selenium WebDriver z poziomu jednego okna. Inną ważną korzyścią ze stosowania systemu Maven jest możliwość uzyskania wszystkich plików bibliotek Selenium wraz z ich zależnościami poprzez skonfigurowanie pliku pom.xml. Podczas budowania projektu Maven automatycznie pobiera niezbędne pliki z repozytorium.

W tej recepturze pokażę, jak skonfigurować systemy Eclipse i Maven do tworzenia testów Selenium WebDriver. Większość kodu zamieszczonego w książce opracowano z wykorzystaniem systemów Eclipse i Maven.

Przygotuj się

Do skonfigurowania środowiska tworzenia testów potrzebne będą systemy Eclipse i Maven. Pobierz Maven pod adresem http://maven.apache.org/download.html i zainstaluj go. Postępuj zgodnie z instrukcjami na stronie pobierania systemu Maven (zapoznaj się z instrukcjami instalacji dostępnymi na tej stronie w sekcji Install).

Pobierz środowisko Eclipse IDE for Java Developers pod adresem https://eclipse.org/downloads/ i zainstaluj je.

Przykłady zamieszczone w tej książce zbudowano w środowisku Eclipse for Java Developers w wersji 4.4.2 (o nazwie kodowej Luna). W tej wersji wtyczka Maven jest dostarczana razem z innymi pakietami.

Jak to zrobić?

Aby skonfigurować Eclipse z Maven w celu tworzenia testów Selenium WebDriver, wykonaj następujące czynności.

Uruchom Eclipse IDE.

Utwórz nowy projekt, wybierając z głównego menu Eclipse polecenie File/New/Other.

W oknie dialogowym New wybierz Maven/Maven Project, tak jak pokazano na poniższym zrzucie ekranu, i kliknij przycisk Next.

Wyświetli się okno dialogowe New Maven Project. Zaznacz pole wyboru Create a simple project (skip archetype selection) i kliknij przycisk Next, tak jak pokazano na pierwszym zrzucie ekranu poniżej.

W oknie dialogowym New Maven Project, w polu tekstowym Group Id: wprowadź com.secookbook.examples, a w polu tekstowym Artifact Id: wpisz SeleniumCookbook. Opcjonalnie możesz także wprowadzić nazwę i opis. Kliknij przycisk Finish, tak jak pokazano na drugim zrzucie ekranu poniżej.

Eclipse utworzy projekt SeleniumCookbook (w eksploratorze pakietu) o strukturze podobnej do tej, którą przedstawiono na poniższym zrzucie ekranu.

W eksploratorze pakietu wybierz plik pom.xml. Spowoduje to otwarcie pliku pom.xml w obszarze edytora z otwartą zakładką Overview. Wybierz zakładkę pom.xml obok zakładki Overview, tak jak pokazano na poniższym zrzucie ekranu.

Dodaj do węzła <project> w pliku pom.xml zależności WebDriver i JUnit podświetlone w poniższym fragmencie kodu.

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>com.secookbook.examples</groupId>

    <artifactId>SeleniumCookbook</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <dependencies>

        <dependency>

            <groupId>org.seleniumhq.selenium</groupId>

            <artifactId>selenium-java</artifactId>

            <version>2.47.1</version>

            <scope>test</scope>

        </dependency>

        <dependency>

            <groupId>junit</groupId>

            <artifactId>junit</artifactId>

            <version>4.12</version>

            <scope>test</scope>

        </dependency>

    </dependencies>

</project>

Najbardziej aktualne informacje na temat zależności dla Selenium WebDriver i JUnit można znaleźć pod adresami — odpowiednio — http://seleniumhq.org/download/maven.html i http://maven.apache.org/plugins/maven-surefire-plugin/examples/junit.html.

TestNG to kolejny, powszechnie używany framework testów jednostkowych w świecie Javy. Aby dodać do projektu obsługę TestNG zamiast JUnit, można uzyskać właściwy wpis dla systemu Maven pod adresem http://testng.org/doc/maven.html.

Wybierz src/test/java w eksploratorze pakietu i kliknij prawym przyciskiem myszy, aby wyświetlić menu. Wybierz New/Class, tak jak pokazano na poniższym zrzucie ekranu.

Pobieranie przykładowego kodu

Przykładowy kod do tej książki możesz pobrać pod adresem ftp://ftp.helion.pl/przyklady/seler2.zip.

W polu tekstowym Package: wpisz com.seleniumcookbook.examples.chapter01, w polu Name: wpisz GoogleSearchTest. Następnie kliknij przycisk Finish, tak jak pokazano na poniższym zrzucie ekranu.

Spowoduje to utworzenie klasy GoogleSearchTest.java w pakiecie com.secookbook. examples.chapter01.

Dodaj do klasy GoogleSearchTest następujący kod.

package com.secookbook.examples.chapter01;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.support.ui.ExpectedCondition;

import org.openqa.selenium.support.ui.WebDriverWait;

import org.junit.*;

import static org.junit.Assert.*;

public class GoogleSearchTest {

    private WebDriver driver;

    @Before

    public void setUp() {

        // Uruchom nowy egzemplarz przeglądarki Firefox

        driver = new FirefoxDriver();

        // Zmaksymalizuj okno przeglądarki

        driver.manage().window().maximize();

        // Przejdź do serwisu Google

        driver.get("http://www.google.com");

    }

    @Test

    public void testGoogleSearch() {

        // Znajdź element wprowadzania tekstu za pomocą jego nazwy

        WebElement element = driver.findElement(By.name("q"));

        // Wyczyść tekst zapisany w elemencie

        element.clear();

        // Wpisz informacje do szukania

        element.sendKeys("Selenium testing tools cookbook");

        // Prześlij formularz

        element.submit();

        // Wyszukiwanie w Google jest renderowane dynamicznie za pomocą JavaScript.

        // Poczekaj na załadowanie strony. Timeout po 10 sekundach

        new WebDriverWait(driver, 10).until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver d) {

                return d.getTitle().toLowerCase()

                .startsWith("selenium testing tools cookbook");

            }

        });

        assertEquals("Selenium testing tools cookbook - Google

Search",

        driver.getTitle());

    }

    @After

    public void tearDown() throws Exception {

        // Zamknij przeglądarkę

        driver.quit();

    }

}

Pobieranie przykładowego kodu

Przykładowy kod do tej książki możesz pobrać pod adresem ftp://ftp.helion.pl/przyklady/seler2.zip. Przykładowy kod w wersji oryginalnej jest również dostępny w repozytorium https://github.com/upgundecha/secookbook.

Aby uruchomić testy w środowisku Maven, wybierz w oknie Package Explorer pakiet SeleniumCookbook. Kliknij prawym przyciskiem myszy nazwę projektu i wybierz polecenie Run As/Maven test. Maven uruchomi wszystkie testy w projekcie.

Jak to działa?

Środowisko Eclipse z zainstalowaną wtyczką Maven umożliwia łatwe tworzenie projektów testów Selenium WebDriver. Dzięki niemu nie trzeba martwić się takimi zadaniami jak zarządzanie konfiguracją projektu, strukturą katalogów, zarządzanie zależnościami i tak dalej. Dostępny jest także zaawansowany edytor kodu umożliwiający pisanie kodu testów.

Po skonfigurowaniu projektu przy użyciu Maven w Eclipse powstaje plik pom.xml zawierający definicje konfiguracji projektu wraz z jego strukturą. Plik zawiera także zależności niezbędne do budowania, testowania i uruchamiania kodu. Przykładowo poniższy kod przedstawia informacje o zależnościach Selenium WebDriver wprowadzonych w pliku pom.xml.

<dependency>

    <groupId>org.seleniumhq.Selenium</groupId>

    <artifactId>selenium-java</artifactId>

    <version>2.47.1</version>

</dependency>

W większości projektów open source informacje te są publikowane w witrynach internetowych projektów. W tym przypadku można je znaleźć pod adresem http://seleniumhq.org/download/maven.html. Można je także uzyskać w witrynie Maven Central, pod adresem http://search.maven.org/ - browse. Maven automatycznie pobierze biblioteki i pliki pomocnicze dla wszystkich wymienionych zależności i doda je do projektu bez konieczności wyszukiwania, pobierania i instalowania tych plików w projekcie. Pozwala to zaoszczędzić wiele czasu i wysiłku podczas zarządzania zadaniami związanymi z zależnościami.

Maven generuje również standardową strukturę katalogów dla kodu, co pozwala na łatwiejsze zarządzanie nim oraz prostsze utrzymanie. W poprzednim przykładzie został utworzony folder src/test/java dla kodu testów oraz folder src/test/resources przeznaczony na zasoby potrzebne do testowania, takie jak pliki danych dla testu, narzędzia i tak dalej.

Maven udostępnia kroki cyklu życia, takie jak tworzenie kodu testów i ich uruchamianie. Środowisko Maven pozwala na utrzymywanie w jednym miejscu kodu aplikacji wraz z kodem testów. W prezentowanym przykładzie Maven zapewnia budowanie kodu aplikacji, a następnie uruchomienie testów i wydanie produkcyjnej wersji oprogramowania.

Co dalej?

Maven można również wykorzystać w celu uruchamiania testów z wiersza polecenia. Aby uruchomić testy z wiersza polecenia, przejdź do folderu projektu SeleniumCookbook w wierszu polecenia, a następnie wpisz poniższe polecenie.

mvn clean test

Polecenie spowoduje przejście przez wszystkie podkatalogi i uruchomienie polecenia clean, które usuwa wcześniejsze pliki kompilacji. Następnie zostanie zbudowany projekt i uruchomione testy. Na koniec w wierszu polecenia wyświetlą się wyniki egzekucji testów, tak jak pokazano na poniższym zrzucie ekranu.

Wykorzystanie narzędzia Ant do egzekucji testów Selenium WebDriver

Apache Ant to popularne narzędzie budowania aplikacji dostępne dla programistów Javy. Jest podobne do Apache Maven, ale w odróżnieniu od Maven nie obsługuje zarządzania projektem i zależnościami. Jest to narzędzie służące wyłącznie do budowania.

Testy Selenium WebDriver można uruchomić za pomocą narzędzia Ant z wiersza polecenia lub za pośrednictwem narzędzi ciągłej integracji (CI), takich jak Jenkins.

W tej recepturze dodam obsługę Ant do projektu SeleniumCookbook utworzonego w recepturze „Konfigurowanie środowiska tworzenia testów Selenium WebDriver dla języka Java z Eclipse i Maven.

Przygotuj się

Narzędzie Ant dla różnych platform systemów operacyjnych można pobrać pod adresem http://ant.apache.org/bindownload.cgi.

Użytkownicy systemu Windows mogą pobrać i zainstalować program WinAnt. Jest on dostarczany wraz z instalatorem, który automatycznie skonfiguruje środowisko Ant. Instalator WinAnt jest dostępny pod adresem http://code.google.com/p/winant/.

W tej recepturze użyto programu WinAnt w systemie operacyjnym Windows.

Potrzebne będą pliki JAR frameworków Selenium WebDriver oraz JUnit. Plik JAR Selenium można pobrać pod adresem http://selenium-release.storage.googleapis.com/, natomiast plik JAR pakietu JUnit — pod adresem https://github.com/junit-team/junit/wiki/Download-and-Install.

Jak to zrobić?

Skonfiguruj projekt SeleniumCookbook utworzony w poprzedniej recepturze do wykorzystania z programem Ant, wykonując następujące kroki.

Utwórz folder lib i skopiuj do niego pliki JAR dla zależności używanych dla tego projektu — czyli Selenium WebDriver i JUnit — tak jak pokazano na poniższym zrzucie ekranu.

Utwórz w folderze projektu plik build.xml o następującej zawartości.

<?xml version="1.0" encoding="UTF-8"?>

<project name="test" default="exec" basedir=".">

    <property name="src" value="./src" />

    <property name="lib" value="./lib" />

    <property name="bin" value="./bin" />

    <property name="report" value="./report" />

    <path id="test.classpath">

        <pathelement location="${bin}" />

        <fileset dir="${lib}">

            <include name="**/*.jar" />

        </fileset>

    </path>

    <target name="init">

        <delete dir="${bin}" />

        <mkdir dir="${bin}" />

    </target>

    <target name="compile" depends="init">

        <javac source="1.8" srcdir="${src}" fork="true"

            destdir="${bin}" >

            <classpath>

                <pathelement path="${bin}" />

                <fileset dir="${lib}">

                    <include name="**/*.jar" />

                </fileset>

            </classpath>

        </javac>

    </target>

    <target name="exec" depends="compile">

        <delete dir="${report}" />

        <mkdir dir="${report}" />

            <mkdir dir="${report}/xml" />

        <junit printsummary="yes" haltonfailure="no">

            <classpath>

                 <pathelement location="${bin}" />

                 <fileset dir="${lib}">

                     <include name="**/*.jar" />

                 </fileset>

            </classpath>

            <test name="com.secookbook.examples.chapter1.GoogleSearchTest"

                haltonfailure="no" todir="${report}/xml"

                outfile="TEST-result">

                <formatter type="xml" />

            </test>

        </junit>

        <junitreport todir="${report}">

            <fileset dir="${report}/xml">

                <include name="TEST*.xml" />

            </fileset>

            <report format="frames"

                todir="${report}/html" />

        </junitreport>

    </target>

</project>

Używając wiersza polecenia, przejdź do katalogu projektu i wpisz następujące polecenie.

ant

Spowoduje to rozpoczęcie procesu budowania. Zostaną uruchomione testy. Na koniec Ant utworzy w folderze projektu folder report. Aby wyświetlić wyniki, przejdź do podfolderu html w folderze report i otwórz plik index.html.

Jak to działa?

Ant potrzebuje pliku build.xml zawierającego ustawienia konfiguracji oraz definicje kroków wymaganych do zbudowania projektu. Do pliku build.xml można dodać kroki opisujące generowanie raportów, wysyłanie powiadomień e-mailem i tak dalej. Ant zapewnia bardzo dynamiczny framework definiowania kroków procesu budowania.

Ant wymaga również skopiowania do folderu lib niezbędnych do zbudowania projektu bibliotek (plików JAR).

Ant skanuje cały zestaw testów w projekcie i uruchamia te testy w sposób podobny do tego, w jaki uruchamia je Maven.

Konfigurowanie programu Microsoft Visual Studio do tworzenia testów Selenium WebDriver

Selenium WebDriver zawiera powiązania .NET umożliwiające rozwijanie testów Selenium na platformie .NET. Aby użyć Selenium WebDriver API wraz z .NET, trzeba zdefiniować w projekcie referencje do bibliotek Selenium WebDriver. Microsoft Visual Studio jest głównym środowiskiem IDE używanym w świecie .NET. Konfigurowanie Selenium WebDriver stało się łatwiejsze za sprawą zastosowania menedżera pakietów NuGet (http://nuget.org/).

W tej recepturze objaśniam proces konfigurowania frameworka Selenium WebDriver w Microsoft Visual Studio 2013 za pośrednictwem menedżera NuGet.

Przygotuj się

Menedżer pakietów NuGet jest dostarczany z środowiskiem Microsoft Visual Studio od wersji 2012. Dla środowiska Microsoft Visual Studio 2010 menedżer NuGet należy pobrać pod adresem http://nuget.codeplex.com i samodzielnie zainstalować.

Jak to zrobić?

Aby skonfigurować Microsoft Visual Studio 2013 do tworzenia testów Selenium WebDriver, wykonaj następujące czynności.

Uruchom program Microsoft Visual Studio.

Utwórz nowy projekt, wybierając z menu głównego polecenie File/New/Project.

W oknie dialogowym New Project wybierz Visual C#/Test/Unit Test Project. Nadaj projektowi nazwę SeleniumCookbook, a następnie kliknij przycisk OK, tak jak pokazano na poniższym zrzucie ekranu.

Teraz za pośrednictwem menedżera NuGet dodaj pakiety Selenium WebDriver. W oknie Solution Explorer kliknij prawym przyciskiem solucję SeleniumCookbook, a następnie wybierz polecenie Manage NuGet Packages…, tak jak pokazano na pierwszym zrzucie ekranu poniżej.

W oknie dialogowym SeleniumCookbook — Manage NuGet Packages wybierz Online i poszukaj pakietu WebDriver. W wyniku wyszukiwania wyświetlą się sugestie pokazane na drugim zrzucie ekranu poniżej.

Wybierz z listy Selenium WebDriver i kliknij przycisk Install. Powtórz ten krok dla pakietu Selenium WebDriver Support Classes. Po pomyślnej instalacji obok obu pakietów wyświetli się zielony znak zaznaczenia, tak jak pokazano na trzecim zrzucie ekranu poniżej.

Zamknij okno dialogowe SeleniumCookbook — Manage NuGet Packages.

W oknie Solution Explorer rozwiń drzewo References dla solucji SeleniumCookbook. Do projektu WebDriver są dodane referencje, takie jakie pokazano na poniższym zrzucie ekranu.

Projekt SeleniumCookbook jest gotowy do tworzenia testów. Możesz teraz w miarę potrzeb dodawać nowe testy.

Jak to działa?

Menedżer pakietów NuGet dodaje zewnętrzne zależności do projektów programu Microsoft Visual Studio. Wyświetla listę wszystkich dostępnych pakietów, po czym automatycznie pobiera i konfiguruje pakiety potrzebne dla projektu. Ponadto automatycznie instaluje zależności dla wybranych pakietów. To duża oszczędność wysiłku podczas wstępnego konfigurowania projektów.

Konfigurowanie frameworka Selenium WebDriver dla Pythona i Ruby

Framework Selenium WebDriver, oprócz Javy i C#, może być również używany z kilkoma innymi językami programowania. Wśród nich do tworzenia testów Selenium WebDriver często są wybierane języki Python i Ruby. W tej recepturze pokazałem sposób instalacji bibliotek klienckich Selenium WebDriver dla języków Python i Ruby.

Przygotuj się

Przed zainstalowaniem biblioteki klienckiej Selenium WebDriver trzeba zadbać o zainstalowanie języków Python i Ruby.

Instalowanie Pythona

Najnowszą wersję Pythona można pobrać pod adresem https://www.python.org/. Pliki po pobraniu należy zainstalować. W tej recepturze użyto Pythona w wersji 3.4.

Instalowanie Ruby

Należy pobrać i zainstalować najnowszą wersję Ruby. Instalator jest dostępny pod adresem https://rubyinstaller.org/downloads/. W tej recepturze wykorzystano Ruby w wersji 2.3.3.

Jak to zrobić?

Instalacja i konfiguracja Selenium WebDriver dla języków Python i Ruby sprowadza się do wykonania poniższych czynności.

Instalowanie Selenium WebDriver dla języka Python

Aby zainstalować Selenium WebDriver z Pythonem, można skorzystać z narzędzia pip oraz następującego wiersza polecenia.

pip install selenium

Spowoduje to pobranie najnowszej wersji biblioteki klienckiej Selenium WebDriver dla Pythona. To wszystko. Zbudujemy prosty test w Pythonie, używając tej instalacji. Utwórz plik google_search.py w swoim ulubionym edytorze lub środowisku IDE i skopiuj poniższy kod.

import unittest

from selenium.webdriver.support import expected_conditions

from selenium import webdriver

from selenium.webdriver.support.ui import WebDriverWait

class GoogleSearch(unittest.TestCase):

    def setUp(self):

        self.driver = webdriver.Firefox()

        self.driver.implicitly_wait(30)

        self.base_url = "https://www.google.com/"

    def test_google_search(self):

        driver = self.driver

        driver.get(self.base_url)

        element = driver.find_element_by_idname("q")

        element.clear()

        element.send_keys("Selenium i testowanie aplikacji. Receptury")

        element.submit()

        WebDriverWait(driver, 30)\

            .until(expected_conditions.title_contains("Selenium i testowanie aplikacji. Receptury"))

        self.assertEqual(driver.title, " Selenium i testowanie aplikacji.Receptury - Szukaj w Google")

    def tearDown(self):

        self.driver.quit()

if __name__ == "__main__":

    unittest.main(verbosity=2, warnings="ignore")

Ten test można uruchomić za pomocą następującego wiersza polecenia.

python google_search.pyb

Interpreter Pythona uruchomi test. Wyświetli się okno Firefoksa i zostanie wykonana operacja wyszukiwania w serwisie Google.com. Po zakończeniu operacji wyświetlą się wyniki pokazane na poniższym zrzucie ekranu.

Instalowanie Selenium WebDriver z Ruby

Framework Selenium WebDriver można zainstalować z Ruby za pomocą narzędzia gem, korzystając z następującego wiersza polecenia.

gem install selenium-webdriver

Spowoduje to zainstalowanie najnowszej wersji biblioteki klienckiej Selenium WebDriver dla Ruby. To wszystko. Korzystając z tej instalacji, zbudujemy prosty test w Ruby. Utwórz plik google_search.rb w wybranym edytorze lub IDE i skopiuj poniższy kod.

require "selenium-webdriver"

gem "test-unit"

require "test/unit"

class GoogleSearch < Test::Unit::TestCase

    def setup

        @driver = Selenium::WebDriver.for :firefox

        @base_url = "https://www.google.com/"

        @driver.manage.timeouts.implicit_wait = 30

    end

    def test_google_search

        @driver.get(@base_url)

        element = @driver.find_element(:name, "q")

        element.clear

        element.send_keys "Selenium i testowanie aplikacji. Receptury"

        element.submit()

        wait = Selenium::WebDriver::Wait.new(:timeout => 10)

        wait.until { @driver.title.include? "Selenium i testowanie aplikacji.Receptury" }

        assert_equal "Selenium i testowanie aplikacji.Receptury - Szukaj w Google", @driver.title

    end

    def teardown

        @driver.quit

    end

end

Ten test można uruchomić za pomocą następującego wiersza polecenia.

ruby google_search.rb

Interpreter Ruby uruchomi test. Wyświetli się okno Firefoksa i zostanie wykonana operacja wyszukiwania w serwisie Google.com. Po zakończeniu operacji wyświetlą się wyniki pokazane na poniższym zrzucie ekranu.

Jak to działa?

Selenium WebDriver jest dostępny dla różnych języków programowania. Dla każdego obsługiwanego języka programiści Selenium publikują bibliotekę kliencką lub dowiązania dla języka. Biblioteki klienckie zawierają klasy i funkcje Selenium WebDriver, które są potrzebne do tworzenia skryptów automatyzacji.

Biblioteki można zainstalować przy użyciu instalatorów pakietów dostępnych dla odpowiednich języków. W naszym przykładzie użyliśmy narzędzia pip dla Pythona, które przez internet nawiązało połączenie ze źródłem PyPI (ang. Python Package Index), pobrało najnowsze biblioteki klienckie Selenium WebDriver dla Pythona i zainstalowało je w katalogu Pythona. W podobny sposób gem Selenium WebDriver dla języka Ruby jest instalowany za pomocą narzędzia gem.

Konfiguracja programu Internet Explorer Driver Server

W poprzednich recepturach pokazałem, jak zautomatyzować przeglądarkę Firefox. Skorzystanie z przeglądarki Firefox było proste. Aby uruchomić skrypty testów w przeglądarce Internet Explorer, potrzebny jest sterownik InternetExplorerDriver oraz oddzielny program wykonywalny Internet Explorer Driver Server.

W tej recepturze zainstaluję InternetExplorerDriver i utworzę testy funkcji wyszukiwania w programie Internet Explorer.

Przygotuj się

Internet Explorer Driver Server należy pobrać pod adresem http://docs.seleniumhq.org/download/. Jest on dostępny w wersjach 32-bitowej i 64-bitowej.

Po pobraniu archiwum IEDriver server rozpakuj je i skopiuj pliki do tego samego katalogu, w którym są przechowywane skrypty.

Jak to zrobić?

Aby utworzyć test, który wykorzystuje program Internet Explorer Driver Server, wykonaj następujące czynności.

W Eclipse, w folderze src/test/resources projektu SeleniumCookbook utwórz nowy folder o nazwie drivers. Skopiuj plik IEDriverServer.exe do tego folderu, tak jak pokazano na poniższym zrzucie ekranu.

Dodaj nowy test i nadaj mu nazwę GoogleSearchTestOnIE. Wprowadź do niego następujący kod.

package com.secookbook.examples.chapter01;

import org.openqa.selenium.ie.InternetExplorerDriver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.remote.DesiredCapabilities;

import org.openqa.selenium.support.ui.ExpectedCondition;

import org.openqa.selenium.support.ui.WebDriverWait;

import org.junit.*;

import static org.junit.Assert.*;

public class GoogleSearchTestOnIE {

    private WebDriver driver;

    @Before

    public void setUp() {

        System.setProperty("webdriver.ie.driver",

                "src/test/resources/drivers/IEDriverServer.exe");

        DesiredCapabilities caps = DesiredCapabilities.internetExplorer();

        caps.setCapability(

                InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS,

                true);

        // Uruchomienie programu Internet Explorer

        driver = new InternetExplorerDriver(caps);

        // Zmaksymalizuj okno przeglądarki

        driver.manage().window().maximize();

        // Przejdź do Google

        driver.get("http://www.google.com");

    }

    @Test

    public void testGoogleSearch() {

        // Znajdź pole tekstowe za pomocą jego nazwy

        WebElement element = driver.findElement(By.name("q"));

        // Wpisz coś do wyszukiwania

        element.sendKeys("Selenium i testowanie aplikacji. Receptury");

        // Teraz prześlij formularz. WebDriver znajdzie

        // formularz odpowiadający elementowi

        element.submit();

        // Strona wyszukiwania w Google jest dynamicznie renderowana za pomocą JavaScript.

        // Czekamy na załadowanie się strony. Timeout po 10 sekundach

        new WebDriverWait(driver, 10).until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver d) {

                return d.getTitle().toLowerCase()

                        .startsWith("Selenium i testowanie aplikacji. receptury");

            }

        });

        assertEquals("Selenium i testowanie aplikacji. Receptury - Szukaj w Google",

                driver.getTitle());

    }

    @After

    public void tearDown() throws Exception {

        // Zamknij przeglądarkę

        driver.quit();

    }

}

Uruchom test. Zobaczysz, jak otworzy się okno programu Internet Explorer i wykonają się kolejne kroki testu.

Jak to działa?

Internet Explorer Driver Server to autonomiczny plik wykonywalny serwera, który implementuje protokół JSON WebDriver. Spełnia rolę spoiwa pomiędzy skryptem testu a przeglądarką Internet Explorer. Relacje pomiędzy poszczególnymi elementami pokazano na poniższym diagramie.

W testach przed utworzeniem egzemplarza przeglądarki Internet Explorer należy określić ścieżkę pliku wykonywalnego IEDriverServer. Aby to zrobić, trzeba ustawić właściwość webdriver.ie.driver, tak jak pokazano w poniższym kodzie.

System.setProperty("webdriver.ie.driver", "src/test/resources/drivers/IEDriverServer.exe");

Ścieżkę możemy również określić na zewnątrz za pomocą opcji Dwebdriver.ie.driver przy użyciu wiersza polecenia Maven. W takim przypadku nie trzeba ustawiać tej właściwości w teście.

Internet Explorer Driver Server obsługuje automatyzację głównych wersji przeglądarki IE w systemach operacyjnych Windows XP, Vista, Windows 7 i Windows 8.

Więcej informacji na temat InternetExplorerDriver można znaleźć pod adresem https://code.google.com/p/selenium/wiki/InternetExplorerDriver.

Trzeba utworzyć egzemplarz klasy InternetExplorerDriver, która będzie odpowiedzialna za nawiązanie połączenia z Internet Explorer Driver Server w celu uruchomienia przeglądarki Internet Explorer, tak jak pokazano poniżej. Następnie przeglądarka uruchomi polecenia Selenium, które będą wywoływane za pomocą różnych metod klas WebDriver i WebElement ze skryptu testu.

DesiredCapabilities caps = new

DesiredCapabilities().internetExplorer();

caps.setCapability(

InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECU

RITY_DOMAINS,

true);

// Uruchomienie przeglądarki Internet Explorer

driver = new InternetExplorerDriver(caps);

Skorzystaliśmy z klasy DesiredCapabilities w celu ustawienia właściwości INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS. Powoduje to zignorowanie ustawień trybu chronionego przeglądarki podczas jej uruchamiania przez IEDriverServer.

Co dalej?

Selenium za pośrednictwem klasy RemoteWebDriver umożliwia uruchamianie testów na zdalnych komputerach. Do wykonywania testów na zdalnym komputerze można skonfigurować dowolną przeglądarkę obsługiwaną przez Selenium. Aby włączyć testy na zdalnym komputerze, trzeba uruchomić na tym komputerze Selenium Server i Internet Explorer Driver Server, a następnie użyć klasy RemoteWebDriverClass, tak jak pokazano w poniższym przykładowym kodzie.

DesiredCapabilities caps = new

DesiredCapabilities().internetExplorer();

caps.setCapability(

InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECU

RITY_DOMAINS,

true);

// Połączenie się z Remote Selenium Server pod określonym adresem URL i z właściwymi opcjami

driver = new RemoteWebDriver(new

URL("http://192.168.1.3:4444/hub/wd"), caps);

Za pomocą opisanej powyżej metody możemy dołączyć do zdalnego serwera dowolną przeglądarkę.

Konfiguracja sterownika ChromeDriver przeglądarki Google Chrome

Aby uruchamiać skrypty testów w przeglądarce Google Chrome, trzeba użyć sterownika ChromeDriver i oddzielnego programu wykonywalnego ChromeDriver (podobnie jak w przypadku przeglądarki Internet Explorer).

Sterownik ChromeDriver jest utrzymywany przez zespół Google Chromium. Więcej informacji na ten temat można znaleźć pod adresem https://sites.google.com/a/chromium.org/chromedriver/.

Spróbuję skonfigurować ChromeDriver i utworzyć test do testowania funkcji wyszukiwania w przeglądarce Google Chrome.

Przygotuj się

Należy pobrać ChromeDriver spod adresu https://sites.google.com/a/chromium.org/chromedriver/downloads.

Jak to zrobić?

Po pobraniu serwera ChromeDriver rozpakuj go i skopiuj pliki do katalogu driver w folderze src/test/resources, tak jak pokazano na poniższym zrzucie ekranu.

W systemach operacyjnych Linux i MacOS plik chromdriver musi być wykonywalny. W tym celu należy skorzystać z polecenia chmod + x nazwa_pliku lub polecenia chmod 775 nazwa_pliku.

Dodaj nowy test i nadaj mu nazwę GoogleSearchTestOnChrome.java. Wprowadź w nim poniższy kod.

package com.secookbook.examples.chapter01;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.support.ui.ExpectedCondition;

import org.openqa.selenium.support.ui.WebDriverWait;

import org.junit.*;

import static org.junit.Assert.*;

public class GoogleSearchTestOnChrome {

    private WebDriver driver;

    @Before

    public void setUp() {

        System.setProperty("webdriver.chrome.driver",

                "./src/test/resources/drivers/chromedriver.exe");

        // Uruchom Chrome

        driver = new ChromeDriver();

        // Zmaksymalizuj okno przeglądarki

        driver.manage().window().maximize();

        // Przejdź do Google

        driver.get("http://www.google.com");

    }

    @Test

    public void testGoogleSearch() {

        // Znajdź pole tekstowe za pomocą jego nazwy

        WebElement element = driver.findElement(By.name("q"));

        // Wpisz coś do wyszukiwania

        element.sendKeys("Selenium i testowanie aplikacji. Receptury");

        // Teraz prześlij formularz. WebDriver znajdzie

        // formularz odpowiadający elementowi

        element.submit();

        // Strona wyszukiwania w Google jest dynamicznie renderowana za pomocą JavaScript.

        // Czekamy na załadowanie się strony. Timeout po 10 sekundach

        new WebDriverWait(driver, 10).until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver d) {

                return d.getTitle().toLowerCase()

                        .startsWith("selenium i testowanie aplikacji. receptury");

            }

        });

        assertEquals("Selenium i testowanie aplikacji. Receptury - Szukaj w Google",

                driver.getTitle());

    }

    @After

    public void tearDown() throws Exception {

        // Zamknij przeglądarkę

        driver.quit();

    }

}

Uruchom test. Otworzy się okno przeglądarki Chrome i zostaną uruchomione wszystkie kroki testu.

Jak to działa?

ChromeDriver jest autonomicznym plikiem wykonywalnym serwera, który implementuje bazujący na JSON protokół komunikacji WebDriver i działa jako spoiwo pomiędzy skryptem testu a Google Chrome, tak jak pokazano na poniższym diagramie.

Więcej informacji na temat ChromeDriver można znaleźć pod adresem https://code.google.com/p/selenium/wiki/ChromeDriver.

Przed utworzeniem egzemplarza przeglądarki Chrome należy określić w testach ścieżkę pliku wykonywalnego ChromeDriver. Można to zrobić przez ustawienie właściwości webdriver.chrome.driver, tak jak pokazano w poniższym kodzie.

System.setProperty("webdriver.chrome.driver","src/test/resources/drivers/chromedriver.exe");

"src/test/resources/drivers/chromedriver.exe")

Ścieżkę można również określić z zewnątrz, za pomocą opcji –Dwebdriver.chrome.driver w wierszu polecenia Maven. W takim przypadku nie trzeba ustawiać tej właściwości w teście. Należy utworzyć egzemplarz klasy ChromeDriver, która nawiąże połączenie z programem ChromeDriver Server, tak jak pokazano w poniższym kodzie. Następnie zostaną uruchomione polecenia Selenium, które będziemy wywoływać ze skryptu testu za pośrednictwem różnych metod klas WebDriver i WebElement.

driver = new ChromeDriver();

Konfiguracja sterownika Microsoft WebDriver dla przeglądarki Microsoft Edge

Microsoft Edge to nowa przeglądarka WWW dostarczana wraz z systemem Microsoft Windows 10. Microsoft Edge implementuje standard W3C WebDriver i posiada wbudowaną obsługę Selenium WebDriver.

Aby uruchamiać skrypty testów w przeglądarce Microsoft Edge, trzeba użyć klasy EdgeDriver i oddzielnego programu wykonywalnego Microsoft WebDriver Server (analogicznie jak w przypadku przeglądarki Internet Explorer).

Microsoft WebDriver Server jest utrzymywany przez zespół programistów Microsoft Edge. Więcej informacji na ten temat można znaleźć pod adresem https://msdn.microsoft.com/en-us/library/mt188085(v=vs.85).aspx.

Spróbujmy skonfigurować Microsoft WebDriver Server i utworzyć test do sprawdzenia funkcji wyszukiwania w przeglądarce Microsoft Edge.

Przygotuj się

Należy pobrać i zainstalować Microsoft WebDriver Server w systemie Windows 10. Adres strony pobierania to https://www.microsoft.com/en-us/download/details.aspx?id=48212.

Jak to zrobić?

Dodaj nowy test i nadaj mu nazwę GoogleSearchTestOnEdge.java. Wprowadź w nim następujący kod.

package com.secookbook.examples.chapter01;

import static org.junit.Assert.*;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.edge.EdgeDriver;

import org.openqa.selenium.edge.EdgeOptions;

import org.openqa.selenium.support.ui.ExpectedCondition;

import org.openqa.selenium.support.ui.WebDriverWait;

public class GoogleSearchTestOnEdge {

    private WebDriver driver;

    @Before

    public void setUp() {

        System.setProperty("webdriver.edge.driver",

                "C:\\Program Files (x86)\\Microsoft Web Driver\\MicrosoftWebDriver.exe");

        EdgeOptions options = new EdgeOptions();

        options.setPageLoadStrategy("eager");

        // Uruchom nowy egzemplarz Edge

        driver = new EdgeDriver(options);

        // Przejdź do Google

        driver.get("http://www.google.com");

    }

    @Test

    public void testGoogleSearch() {

        // Znajdź pole tekstowe za pomocą jego nazwy

        WebElement element = driver.findElement(By.name("q"));

        // Wyczyść istniejącą wartość tekstową

        element.clear();

        // Wpisz coś do wyszukiwania

        element.sendKeys("Selenium i testowanie aplikacji. Receptury");

        WebElement button = driver.findElement(By.name("btnG"));

        button.click();

        // Strona wyszukiwania w Google jest dynamicznie renderowana za pomocą JavaScript.

        // Czekamy na załadowanie się strony. Timeout po 10 sekundach

        new WebDriverWait(driver, 10).until(new ExpectedCondition<Boolean>() {

            public Boolean apply(WebDriver d) {

                return d.getTitle().toLowerCase()

                        .startsWith("Selenium i testowanie aplikacji. receptury");

            }

        });

        assertEquals("Selenium i testowanie aplikacji. Receptury - Szukaj w Google",

                driver.getTitle());

    }

    @After

    public void tearDown() throws Exception {

        // Zamknij przeglądarkę

        driver.quit();

    }

}

Uruchom test. Otworzy się okno przeglądarki Microsoft Edge i zostaną uruchomione wszystkie kroki testu.

Jak to działa?

Microsoft WebDriver Server jest autonomicznym plikiem wykonywalnym serwera, który implementuje bazujący na JSON protokół komunikacyjny WebDriver. Spełnia on rolę spoiwa pomiędzy skryptem testu a przeglądarką Microsoft Edge, tak jak pokazano na poniższym diagramie.

Przed utworzeniem egzemplarza programu Microsoft Edge należy określić w testach ścieżkę do programu wykonywalnego Microsoft Server WebDriver. Można to zrobić za pomocą właściwości webdriver.edge.driver, tak jak pokazano w poniższym kodzie.

System.setProperty("webdriver.edge.driver","C:\\Program Files (x86)\\Microsoft Web

Driver\\MicrosoftWebDriver.exe");

Możemy również określić ścieżkę zewnętrznie za pomocą opcji –Dwebdriver.edge.driver przy użyciu wiersza polecenia Maven. W takim przypadku nie trzeba ustawiać tej właściwości w teście.

Rozdział 2.
Wyszukiwanie elementów

W tym rozdziale nauczysz się korzystania z API Selenium WebDriver do znajdowania elementów na stronach WWW oraz do interakcji z tymi elementami. Poznasz też różne strategie lokalizacji obsługiwane przez Selenium WebDriver. Oto czynności, jakie nauczysz się wykonywać.

Korzystanie z narzędzi przeglądarki do badania elementów i modelu DOM strony.

Wyszukiwanie pojedynczego elementu za pomocą metody findElement.

Wyszukiwanie wielu elementów przy użyciu metody findElements.

Wyszukiwanie linków.

Wyszukiwanie elementów na podstawie nazwy taga (znacznika).

Wyszukiwanie elementów na podstawie selektorów CSS.

Wyszukiwanie elementów z wykorzystaniem XPath.

Wyszukiwanie elementów za pomocą tekstowej zawartości tagów.

Wyszukiwanie elementów przy użyciu zaawansowanych selektorów CSS.

Wyszukiwanie elementów za pomocą selektorów jQuery.

Wprowadzenie

Aplikacje webowe i strony WWW w takich aplikacjach zazwyczaj są pisane z pomocą mieszanki formatów HTML (ang. Hyper Text Markup Language), kaskadowych arkuszy stylów (CSS) i kodu JavaScript. Na podstawie działań użytkownika, takich jak przejście pod adres URL (ang. Uniform Resource Locator) witryny WWW lub kliknięcie przycisku Prześlij, przeglądarka wysyła żądanie do serwera WWW. Proces serwera WWW przetwarza to żądanie i odsyła do przeglądarki odpowiedź zawierającą kod HTML wraz z powiązanymi zasobami, takimi jak kod JavaScript, CSS, ilustracje i tak dalej. Informacje otrzymane z serwera są używane przez przeglądarkę do renderowania strony WWW zawierającej różne elementy wizualne, na przykład pola tekstowe, przyciski, etykiety, tabele, formularze, pola wyboru, przełączniki, listy, ilustracje i tak dalej. Realizując ten proces, przeglądarka ukrywa przed użytkownikiem kod HTML i powiązane z nim zasoby. Użytkownik widzi w oknie przeglądarki jedynie graficzny interfejs.

Ponieważ dążymy do zautomatyzowania interakcji z przeglądarką za pomocą Selenium, trzeba poinformować framework o tym, w jaki sposób programowo znaleźć określone elementy lub zestaw elementów na stronie WWW oraz jak zasymulować działania użytkownika na tych elementach. Selenium oferuje różne metody wyboru lub lokalizacji do wyszukiwania elementów na podstawie kryteriów budowanych z wykorzystaniem wartości atrybutów i selektorów przekazywanych w skrypcie.

Wykorzystywanie narzędzi przeglądarki do badania elementów i struktury strony

Przed przystąpieniem do omawiania sposobów wyszukiwania elementów na stronie należy tę stronę przeanalizować. Wtedy poznamy drzewo modelu DOM (ang. Document Object Model) oraz zapoznamy się z właściwościami i atrybutami elementów wyświetlanych na stronie, sposobem wykonywania z aplikacji wywołań JavaScript oraz AJAX i tak dalej.

Przeglądarki wykorzystują kod HTML strony pisany w celu renderowania w oknie przeglądarki elementów wizualnych. Korzystają również z innych zasobów, w tym z kodu JavaScript, CSS, ilustracji i tak dalej, w celu podjęcia decyzji dotyczącej wyglądu i zachowania tych elementów.

Poniżej zaprezentowano przykład strony aplikacji kalkulatora BMI oraz kod HTML napisany w celu wyrenderowania tej strony w przeglądarce.

Aby obejrzeć kod strony, można kliknąć prawym przyciskiem myszy w oknie przeglądarki, a następnie z rozwijanego menu wybrać polecenie Pokaż źródło strony. Spowoduje to wyświetlenie kodu HTML strony w osobnym oknie, tak jak pokazano na poniższym zrzucie ekranu.

Do wyświetlenia tych informacji w zrozumiały i pozwalający na śledzenie struktury sposób potrzebne są odpowiednie narzędzia. W tej recepturze zwięźle opiszę niektóre spośród tych narzędzi, a następnie przejdę do zagadnień wyszukiwania elementów.

Jak to zrobić?

W poniższych punktach opisane zostaną narzędzia do analizowania elementów oraz drzew DOM strony, wywołań JavaScript i atrybutów stylu CSS. Niektóre z tych narzędzi są wbudowane w przeglądarki, inne są dostępne jako wtyczki.

Badanie strony i jej elementów — przeglądarka Mozilla Firefox z dodatkiem Firebug

Nowsze wersje przeglądarki Mozilla Firefox zawierają wbudowane funkcjonalności do analizowania stron i ich elementów. W tej recepturze będziemy jednak używać dodatku Firebug, który ma bardziej zaawansowane funkcje.

Dodatek Firebug trzeba zainstalować w przeglądarce Firefox. Jest dostępny do ściągnięcia pod adresem https://addons.mozilla.org/enus/firefox/addon/firebug/.

Aby zbadać element na stronie, naprowadź wskaźnik myszy nad żądany element i kliknij go prawym przyciskiem myszy. Otworzy się menu podręczne. Wybierz opcję Zbadaj element za pomocą Firebuga, tak jak pokazano na poniższym zrzucie ekranu.

Wyświetli się okno Firebuga z kodem HTML w postaci drzewa, tak jak pokazano na poniższym zrzucie ekranu.

Firebug pozwala także weryfikować poprawność selektorów XPath lub CSS za pomocą pola wyszukiwania w sekcji Firebug. Wystarczy wpisać wyrażenie XPath lub selektor CSS, a Firebug podświetli element lub elementy, które pasują do wyrażenia, tak jak pokazano na poniższym zrzucie ekranu.

Firebug oferuje różne inne funkcje debugowania. Generuje również wyrażenia XPath i selektory CSS dla elementów. W tym celu zaznacz żądany element w drzewie, kliknij go prawym przyciskiem myszy i z rozwijanego menu wybierz opcję Kopiuj wyrażenie XPath lub Kopiuj ścieżkę CSS, tak jak pokazano na poniższym zrzucie ekranu.

Spowoduje to wklejenie do schowka wyrażenia XPath lub selektora CSS, które są sugerowane przez Firebug.

Badanie stron i elementów — przeglądarka Google Chrome

Google Chrome oferuje wbudowaną funkcję do analizy stron i elementów. Jest ona bardzo podobna do Firebuga, ale ma znacznie więcej możliwości. Wystarczy naprowadzić wskaźnik myszy na żądany element na stronie i kliknąć go prawym przyciskiem myszy. Wyświetli się menu podręczne, z którego należy wybrać opcję Zbadaj. W przeglądarce otworzy się okno Narzędzia dla programistów, które wyświetla informacje podobne do tych z Firebuga, co pokazano na poniższym zrzucie ekranu.

Podobnie jak przy użyciu Firebuga w przeglądarce Firefox, za pomocą narzędzi dla programistów w Google Chrome możemy badać wyrażenia XPath i selektory CSS. Przy otwartej zakładce Elements naciśnij Ctrl+F (w komputerach Mac użyj klawiszy Command+F). Spowoduje to wyświetlenie pola wyszukiwania. Wystarczy wprowadzić wyrażenie XPath lub selektor CSS, aby podświetlić pasujące elementy w drzewie, tak jak pokazano na poniższym zrzucie ekranu.

Narzędzia dla programistów przeglądarki Chrome pozwalają także na określenie wyrażenia XPath dla elementu. W tym celu kliknij żądany element w drzewie prawym przyciskiem myszy i wybierz z menu podręcznego opcję Copy XPath.

Badanie stron i elementów — przeglądarka Microsoft Internet Explorer

Podobnie jak Google Chrome, Microsoft Internet Explorer również zawiera wbudowaną funkcję do analizy stron i ich elementów.

Aby otworzyć narzędzia programistyczne, naciśnij klawisz F12. Wyświetli się sekcja Narzędzia dewelopera, podobna do pokazanej na poniższym zrzucie ekranu.

Aby zbadać element, kliknij ikonę wskaźnika myszy i ustaw kursor nad żądanym elementem na stronie. Wskazany element zostanie wyróżniony niebieską ramką. W drzewie zobaczysz wyświetlony jego kod HTML, tak jak pokazano na zrzucie ekranu poniżej.

Jak to działa?

Narzędzia dla programistów w przeglądarce są bardzo przydatne podczas tworzenia skryptów testów. Narzędzia te pomagają znaleźć szczegóły lokalizatora elementów, z którym trzeba się komunikować w ramach testu. Narzędzia te parsują kod strony i wyświetlają informacje w drzewie hierarchii. Dostarczają także informacje o zastosowanych stylach, zasobach strony, modelu DOM, kodzie JavaScript i tak dalej.

Niektóre z tych narzędzi zapewniają też możliwość uruchamiania kodu JavaScript w celu debugowania i testowania.

W poniższych recepturach omówię różne typy lokalizatorów obsługiwanych przez Selenium WebDriver. Narzędzia te pomagają w wyszukiwaniu oraz decydowaniu o użyciu różnych strategii lub metod dostarczanych przez Selenium WebDriver API.

Wyszukiwanie elementu za pomocą metody findElement

Wyszukiwanie elementów w Selenium WebDriver odbywa się za pomocą metod findElement() i findElements() dostarczonych za pośrednictwem interfejsów WebDriver i WebElement.

Metoda findElement() zwraca egzemplarz klasy WebElement, który można znaleźć w modelu DOM strony na podstawie określonych lokalizatorów nazywanych także kryteriami wyszukiwania. Jeśli określona metoda nie zdoła znaleźć elementu przy użyciu określonego kryterium wyszukiwania, zgłasza wyjątek NoSuchElementFoundException.

Metoda findElements() zwraca listę obiektów WebElement spełniających podane kryteria wyszukiwania. Jeśli elementy nie zostaną znalezione, zwraca pustą listę.

Metody find pobierają argument w postaci lokalizatora lub obiektu kwerendy jako egzemplarza klasy By. Selenium WebDriver dostarcza klasę By, która obsługuje różne strategie lokalizacji. Różne strategie lokalizatorów obsługiwanych przez Selenium WebDriver zestawiono w poniższej tabeli.

Strategia

Składnia

Opis

Na podstawie ID

Java: driver.findElement(By.id(<ID elementu))

C#: driver.FindElement(By.Id(<ID elementu>))

Python: driver.find_element_by_id(<ID elementu>)

Ruby: driver.find_element(:id,<ID elementu>)

Umożliwia znalezienie elementów na podstawie atrybutu ID

Według nazwy

Java: driver.findElement(By.name(<element name>))

C#:driver.FindElement(By.Name(<nazwa_elementu>))

Python: driver.find_element_by_name(<nazwa_elementu>)

Ruby: driver.find_element(:name,<nazwa_elementu>)

Umożliwia znalezienie elementów za pomocą atrybutu Name

Według nazwy klasy

Java: driver.findElement(By.className(<klasa_elementu>))

C#: driver.FindElement(By.ClassName(<klasa_elementu>))

Python: driver.find_element_by_class_name(<klasa_elementu>)

Ruby: driver.find_element(:class,<klasa_elementu>)

Umożliwia znalezienie elementów przy użyciu wartości atrybutu Class

Według nazwy znacznika

Java: driver.findElement(By.tagName(<nazwa_znacznika_html>))

C#: driver.FindElement(By.TagName(<nazwa_znacznika_html>))

Python: driver.find_element_by_tag_name(<nazwa_znacznika_html>)

Ruby: driver.find_element(:nazwa_znacznika,<nazwa_znacznika_html>)

Umożliwia znalezienie elementów na podstawie znacznika HTML

Na podstawie tekstu linka

Java: driver.findElement(By.linkText(<tekst_linka>))

C#: driver.FindElement(By.LinkText(<tekst_linka>))

Python: driver.find_element_by_link_text(<tekst_linka>)

Ruby: driver.find_element(:tekst_linka,<tekst_linka>)

Umożliwia znalezienie łącza na podstawie jego tekstu

Na podstawie częściowego tekstu linka

Java: driver.findElement(By.partialLinkText(<tekst_linka>))

C#: driver.FindElement(By.partialLinkText(<tekst_linka>))

Python: driver.find_element_by_partial_link_text(<tekst_linka>)

Ruby: driver.find_element(:częściowy_tekst_linka,<tekst_linka>)

Pozwala znaleźć linki na podstawie fragmentu tekstu

Na podstawie CSS

Java: driver.findElement(By.cssSelector(<selektor_css>))

C#: driver.FindElement(By.CssSelector(<selektor_css>))

Python: driver.find_elements_by_css_selector (<selektor_css>)

Ruby: driver.find_element(:css,<selektor_css>)

Umożliwia znalezienie elementów na podstawie selektora CSS

Według wyrażenia Xpath

Java: driver.findElement(By.xpath(<wyrażenie_kwerendy_xpath>))

C#: driver.FindElement(By.XPath(<wyrażenie_kwerendy_xpath>))

Python: driver.find_elements_by_xpath(<wyrażenie_kwerendy_xpath>)

Ruby: driver.find_element(:xpath,<wyrażenie_kwerendy_xpath>)

Umożliwia znalezienie elementów na podstawie wyrażenia XPath

W tej recepturze skorzystam z metody findElement() do zlokalizowania elementów.

Jak to zrobić?

Preferowanym sposobem wyszukiwania elementów jest wykorzystanie do tego celu atrybutów id, name lub class. W poniższych podpunktach spróbuję skorzystać z tych metod do zlokalizowania elementów.

Wyszukiwanie elementów według atrybutu ID

Elementy można wyszukiwać na podstawie wartości atrybutu id. Podczas przeszukiwania modelu DOM przeglądarki używają atrybutu id jako preferowanego sposobu identyfikowania elementów. Sposób ten zapewnia najszybszą strategię lokalizowania elementów.

Zobaczysz teraz, jak używać wartości atrybutów id w celu wyszukiwania elementów na formularzu logowania, tak jak pokazano w poniższym kodzie.

<form name="loginForm">

    <label for="username">Nazwa użytkownika: </label> <input type="text"

        id="username" /><br/>

    <label for="password">Hasło: </label> <input

        type="password" id="password" /><br/>

    <input name="login" type="submit" value="Zaloguj się" />

</form>

Aby zlokalizować pola Nazwa użytkownika i Hasło, możemy użyć atrybutu id w następujący sposób.

WebElement username = driver.findElement(By.id("username"));

WebElement password = driver.findElement(By.id("password"));

Wyszukiwanie elementów na podstawie atrybutu Name

W niektórych przypadkach nie można użyć atrybutu id z następujących przyczyn:

nie wszystkie elementy na stronie mają określony atrybut id;

atrybuty id nie są określone dla kluczowych elementów na stronie;

wartości atrybutu id są generowane dynamicznie.

W tym przykładzie dla elementów formularza logowania wykorzystano atrybut name zamiast atrybutu id.

<form name="loginForm">

    <label for="username">Nazwa użytkownika: </label> <input type="text"

        name="username" /><br/>

    <label for="password">Hasło: </label> <input

        type="password" name="password" /><br/>

    <input name="login" type="submit" value="Zaloguj się" />

</form>

Atrybut name można wykorzystać do wyszukiwania elementów w następujący sposób.

WebElement username = driver.findElement(By.name("username"));

WebElement password = driver.findElement(By.name("password"));

Na stronie może być wiele elementów z podobną wartością atrybutu name. W takim przypadku zostanie zwrócony pierwszy element na stronie o określonej wartości. Nie zawsze będzie to ten element, którego szukamy. Może to spowodować niepowodzenie testu.

Należy zalecić programistom, aby podczas tworzenia aplikacji dodawali dla kluczowych elementów atrybut id, a także inne unikatowe atrybuty. Pozwoli to na łatwe wyszukiwanie elementów. Ponadto takie postępowanie znacznie poprawia możliwości testowania.

Wyszukiwanie elementów na podstawie atrybutu Class

Do wyszukiwania elementów, oprócz atrybutów id i name, możemy również użyć atrybutu class. Atrybut class jest powszechnie wykorzystywany w celu zastosowania stylu CSS do elementu.

W tym przykładzie dla elementów formularza logowania wykorzystano atrybut class zamiast atrybutu id.

<form name="loginForm">

    <label for="username">Nazwa użytkownika: </label> <input type="text"

        class="username" /></br>

    <label for="password">Hasło: </label> <input

        type="password" class="password" /></br>

    <input name="login" type="submit" value="Zaloguj się" />

</form>

Atrybut class można wykorzystać do wyszukiwania elementów w następujący sposób.

WebElement username =

    driver.findElement(By.className("username"));

WebElement password =

    driver.findElement(By.className("password"));

Czasami dla pojedynczego elementu podajemy wiele klas CSS. Oto przykład.

<input type="text"

    class="username textfield" />

W takim przypadku można użyć nazwy jednej z klas za pośrednictwem metody className().

Jak to działa?

Selenium WebDriver dostarcza metodę findElement(), która pozwala znaleźć te elementy testowanej strony, które są wymagane w skrypcie testu.

Podczas wyszukiwania elementu metoda ta, stosując podaną strategię lokalizatora, przegląda model DOM w poszukiwaniu pasujących elementów, a następnie zwraca do testu pierwszy pasujący element.

Co dalej?

Interfejs WebElement obsługuje również metody find do wyszukiwania elementów potomnych. Jeśli na przykład niektóre elementy na stronie są zdublowane, ale umieszczone w oddzielnych elementach <div>, wtedy możemy najpierw zlokalizować nadrzędny element <div>, a następnie podrzędny element w kontekście elementu nadrzędnego w następujący sposób.

WebElement div = driver.findElement(By.id("div1"));

WebElement topLink = div.findElement(By.linkText("top"));

Można również użyć skróconej metody w następujący sposób.

WebElement topLink = driver.findElement

    (By.id("div1")).findElement(By.linkText("top"));

NoSuchElementException

Jeśli nie można znaleźć żądanego elementu przy użyciu określonej strategii lokalizatora, metody findElement() zgłaszają wyjątek NoSuchElementException. Gdy metoda findElements() nie znajdzie elementów pasujących do lokalizatora, zwraca pustą listę.

Zobacz też

Receptura „Wyszukiwanie wielu elementów za pomocą metody findElements”.

Wyszukiwanie wielu elementów za pomocą metody findElements

Selenium WebDriver oferuje metodę findElements(), za pomocą której można znaleźć więcej niż jeden element pasujący do podanych kryteriów wyszukiwania. Metoda ta jest przydatna, gdy chcemy pracować z grupą podobnych do siebie elementów. Możemy na przykład znaleźć wszystkie linki, które są wyświetlane na stronie, pobrać wszystkie wiersze z tabeli i tak dalej.

W tej recepturze za pomocą metody findElements() pobiorę wszystkie linki i wyświetlę ich adresy docelowe.

Jak to zrobić?

Utworzymy test, który będzie pobierał wszystkie linki ze strony, sprawdzał ich liczbę, a następnie dla każdego linka wyświetlał adres docelowy.

@Test

public void testFindElements() {

    // Wyszukanie wszystkich linków, które są wyświetlane na stronie

    List<WebElement> links = driver.findElements(By.tagName("a"));

    // Sprawdzenie, czy na stronie są wyświetlane cztery linki

    assertEquals(4, links.size());

    // Iterowanie po liście linków i wyświetlanie dla każdego z nich

    // adresu docelowego

    for(WebElement link : links) {

        System.out.println(link.getAttribute("href"));

    }

}

Jak to działa?

Metoda findElements() zwraca w postaci listy obiektów WebElement wszystkie elementy pasujące do przekazanych kryteriów wyszukiwania.

List<WebElement> links = driver.findElements(By.tagName("a"));

Metoda size() typu List pozwala stwierdzić, ile elementów jest na liście.

assertEquals(4, links.size());

Po tej liście można iterować, wyświetlając dla każdego linka adres docelowy, tak jak pokazano poniżej.

for(WebElement link : links) {

    System.out.println(link.GetAttribute("href"));

}

Zobacz też

Receptura „Wyszukiwanie elementu za pomocą metody findElement”.

Wyszukiwanie linków

Selenium WebDriver oferuje dwie specjalne metody do wyszukiwania linków na stronie. Linki mogą być wyszukiwane na podstawie ich tekstu lub fragmentu tekstu.

Wyszukiwanie linków na podstawie fragmentu tekstu jest przydatne w przypadku linków z dynamicznym tekstem. W tej recepturze pokażę, jak skorzystać z tych metod do wyszukiwania linków.

Jak to zrobić?

Aby zobaczyć, jak działa wyszukiwanie linków w Selenium WebDriver, utworzymy prosty test korzystający z wymienionych poniżej opcji.

Wyszukiwanie linka na podstawie jego tekstu

Klasa By frameworka Selenium WebDriver zawiera metodę linkText() pozwalającą zlokalizować linki na podstawie wyświetlanego tekstu. W poniższym przykładzie zlokalizujemy link Gmail wyświetlany na stronie głównej serwisu Google.

WebElement gmailLink = driver.findElement(By.linkText("GMail"));

assertEquals("http://mail.google.com/",

    gmailLink.getAttribute("href"));

Wyszukiwanie linka na podstawie fragmentu tekstu

Klasa By frameworka Selenium WebDriver dostarcza również metodę pozwalającą zlokalizować linki na podstawie fragmentu tekstu. Metoda jest przydatna, gdy programiści tworzą linki z dynamicznym tekstem. W poniższym przykładzie dostarczono link otwierający skrzynkę odbiorczą. Link wyświetla również liczbę nowych wiadomości e-mail, która może się dynamicznie zmieniać. W podanym poniżej przykładzie zastosowano metodę partialLinkText() do zlokalizowania łącza na podstawie znanej lub stałej części tekstu łącza. W tym przykładzie będzie to tekst „Inbox”. Oto przykład.

WebElement inboxLink =

    driver.findElement(By.partialLinkText("Inbox"));

System.out.println(inboxLink.getText());

Jak to działa?

Metody lokalizatorów linkText i partialLinkText odpytują sterownik o wszystkie linki, które spełniają podane kryteria tekstowe, a następnie zwracają pasujący link lub dopasowane linki.

Co dalej?

Linków można także szukać za pomocą atrybutów id, name albo class, o ile programiści podali te atrybuty.

Wyszukiwanie elementów na podstawie tekstu może powodować problemy w przypadku testowania aplikacji w wielu wersjach językowych. W takich scenariuszach może się sprawdzić skorzystanie ze sparametryzowanych wartości tekstowych lokalizatorów.

Zobacz też

Receptura „Wyszukiwanie elementu za pomocą metody findElement”,

Receptura „Wyszukiwanie wielu elementów za pomocą metody findElements”.

Wyszukiwanie elementów na podstawie nazwy znacznika

Klasa By frameworka Selenium WebDriver zawiera metodę tagName(), która pozwala wyszukiwać elementy na podstawie nazwy znacznika HTML. Metoda przypomina metodę modelu DOM getElementsByTagName() w języku JavaScript.

Metodę można wykorzystać w celu zlokalizowania elementów na podstawie ich nazwy znacznika — na przykład można zlokalizować wszystkie tagi <tr> w tabeli.

W tej recepturze krótko pokażę, w jaki sposób wykorzystać metodę lokalizatora tagName().

Jak to zrobić?

Załóżmy, że na stronie jest jeden przycisk. Ten przycisk można zlokalizować na podstawie jego znacznika w następujący sposób.

WebElement loginButton = driver.findElement(By.tagName("button"));

loginButton.click();

Weźmy kolejny przykład, w którym chcemy policzyć, ile wierszy jest wyświetlanych w elemencie <table>. Można to zrobić w następujący sposób.

WebElement table = driver.findElement(By.id("summaryTable"));

List<WebElement> rows = table.findElements(By.tagName("tr"));

assertEquals(10, rows.size());

Jak to działa?

Metoda lokalizatora tagName() odpytuje model DOM i zwraca listę pasujących elementów dla określonej nazwy znacznika. Metoda ta może nie być wiarygodna podczas lokalizacji pojedynczych elementów, gdy na stronie istnieje wiele egzemplarzy tych elementów.

Zobacz też

Receptura „Wyszukiwanie wielu elementów za pomocą metody findElements”.

Wyszukiwanie elementów z wykorzystaniem XPath

XPath (język ścieżek XML) to język zapytań używany do wybierania węzłów z dokumentów XML. Wszystkie najważniejsze przeglądarki implementują specyfikację DOM Level 3 XPath (za pośrednictwem linka http://www.w3.org/TR/DOM-Level-3-XPath/), co zapewnia dostęp do drzewa modelu DOM.

Język XPath bazuje na reprezentacji dokumentu XML w formie drzewa. Zapewnia możliwość poruszania się po drzewie i wybierania węzłów z wykorzystaniem różnych kryteriów.

Selenium WebDriver obsługuje XPath w zakresie lokalizowania elementów na podstawie wyrażeń XPath znanych również jako kwerendy XPath.

Jedna z istotnych różnic pomiędzy XPath a CSS polega na tym, że za pomocą wyrażeń XPath możemy wyszukiwać elementy w hierarchii modelu DOM wstecz lub w przód, podczas gdy CSS działa tylko w kierunku do przodu. Oznacza to, że posługując się wyrażeniami XPath, można lokalizować elementy nadrzędne za pomocą elementów potomnych i vice versa.

W tej recepturze omówię kilka podstawowych kwerend XPath do lokalizowania elementów. Następnie przeanalizuję kilka zaawansowanych kwerend XPath.

Dokumenty XML są traktowane jak drzewa złożone z węzłów. Element najwyższego poziomu w drzewie nazywa się korzeniem (ang. root element). Po załadowaniu dokumentu HTML do modelu DOM dostępne jest podobne drzewo węzłów. Oto przykład strony HTML.

<html>

    <head>

        <title>Moja lista książek</title>

    </head>

<body>

    <h1>Moja lista książek</h1>

    <div>

        <table class="main-list">

            <tr>

                <td>Tytuł</td>

                <td>Autor</td>

                <td>Rok wydania</td>

                <td>Cena</td>

                <td>Strona książki</td>

            </tr>

            <tr id="book_1">

                <td>XML Developer's Guide</td>

                <td>Gambardella, Matthew</td>

                <td>Rok wydania</td>

                <td class="price">44.95</td>

                <td><div class="desc">Dogłębne spojrzenie na tworzenie aplikacji z wykorzystaniem XML.</div></td>

                <td><a href="/book_1.html">

                    <img src="/img/book1_png/" alt="XML Developers Guide">

                    </a></td>

                </tr>

        </table>

    </div>

</body>

</html>

Zanim przejdę do korzystania z XPath, omówię kilka podstawowych terminów związanych z tą technologią. W roli przykładu będę używać wymienionego wyżej dokumentu HTML.

Termin

Opis

Węzły

DOM reprezentuje dokument HTML jako drzewo węzłów. Oto przykłady węzłów z poprzedniego dokumentu HTML:

html: węzeł reprezentujący korzeń (element główny),

title: węzeł elementu,

id="b00k_1": węzeł reprezentujący atrybuty i wartości.

Element najwyższego poziomu w drzewie nazywa się korzeniem lub węzłem głównym.

Wartości niepodzielne

Wartości niepodzielne to węzły bez dzieci i rodziców, na przykład:

Gambardella, Matthew

XML Developer's Guide

44.95

Rodzice

Każdy element i atrybut ma jednego rodzica. Przykładowo element body jest rodzicem elementu div. Na podobnej zasadzie div jest rodzicem elementu table.

Dzieci

Węzły elementów mogą mieć zero, jedno lub więcej dzieci. Przykładowo są dwa elementy tr, które są dziećmi elementu table.

Rodzeństwo

Węzły, które mają tego samego rodzica. Przykładowo h1 i div to rodzeństwo, a ich rodzicem jest element body.

Przodkowie

Rodzic węzła, rodzic rodzica i tak dalej. Przykładowo przodkami elementu table są div, body i html.

Potomkowie

Dzieci węzłów, ich dzieci i tak dalej. Przykładowo potomkami elementu table są tr, td i div.

Wybieranie węzłów

Do wybierania węzłów drzewa XPath używa wyrażeń ścieżek. Węzeł jest wybierany w wyniku podążania ścieżką. Najbardziej przydatne wyrażenia ścieżek zestawiono w poniższej tabeli.

Wyrażenie

Opis

nazwa_węzła

Wybiera wszystkie węzły z nazwą nazwa_węzła. Przykładowo ścieżka table spowoduje wybranie wszystkich elementów table.

/ (ukośnik)

Wybiera element lub elementy względne w stosunku do elementu głównego, na przykład:

/html: wybiera główny element HTML; ukośnik (/) użyty na początku definiuje ścieżkę bezwzględną,

html/body/table: wybiera wszystkie elementy table, które są dziećmi HTML.

Ukośnik (/) użyty na początku kodu elementu definiuje ścieżkę bezwzględną. Użyty w środku definiuje relacje przodka i potomków, na przykład /div/table zwraca element div zawierający obiekt table.

// (podwójny ukośnik)

Wybiera w dokumencie węzeł lub węzły spełniające kryteria, niezależnie od jego położenia, na przykład:

//table wybierze wszystkie elementy table bez względu na to, gdzie znajdują się w dokumencie,

//tr//td wybiera wszystkie elementy td,

//a//img wybiera wszystkie elementy img, które są dziećmi elementu a (kotwica).

Podwójny ukośnik (//) użyty w środku definiuje relację potomków, na przykład html /html//title zwraca element title, który jest potomkiem elementu html.

. (kropka)

Reprezentuje bieżący węzeł.

.. (podwójna kropka)

Wybiera rodzica bieżącego węzła, na przykład //table/.. zwraca element div.

@

Reprezentuje atrybut, na przykład:

//@id: wybiera wszystkie elementy, gdzie zdefiniowano atrybut id bez względu na to, gdzie one są w dokumencie,

//img/@alt: wybiera wszystkie elementy img, gdzie zdefiniowano atrybut @alt.

Jak to zrobić?

Przeanalizujmy kilka podstawowych wyrażeń XPath, które mogą być używane we frameworku Selenium WebDriver. Framework dostarcza metodę xpath(). Jej zadaniem jest lokalizowanie elementów na podstawie wyrażenia XPath.

Wyszukiwanie elementów na podstawie ścieżki bezwzględnej

Ścieżki bezwzględne XPath odwołują się do konkretnej lokalizacji elementu wewnątrz całej hierarchii modelu DOM. Oto przykład, w którym na podstawie ścieżki bezwzględnej wyszukano pole tekstowe z nazwą użytkownika. Przy podawaniu ścieżki bezwzględnej pomiędzy elementami wprowadza się spacje.

WebElement userName = driver.findElement(By.xpath("html body div div form input"));

Strategia ta ma jednak ograniczenia, ponieważ zależy od struktury lub hierarchii elementów na stronie. Jeśli to się zmieni, wtedy lokalizator nie zdoła znaleźć elementu.

Wyszukiwanie elementów na podstawie ścieżki względnej

Posługując się ścieżką względną, można zlokalizować element bezpośrednio, niezależnie od jego lokalizacji w modelu DOM. Jeśli przykładowo założymy, że pole tekstowe do wprowadzania nazwy użytkownika jest pierwszym elementem <input> w modelu DOM, można je znaleźć w następujący sposób.

WebElement userName = driver.findElement(By.xpath("//input"));

Wyszukiwanie elementów na podstawie predykatów

Predykat jest osadzony w nawiasach kwadratowych. Służy do wyszukiwania określonego węzła lub węzłów, które zawierają podaną wartość.

W poprzednim przykładzie zapytanie XPath zwróci pierwszy element <input> znaleziony w modelu DOM. W modelu DOM może być wiele elementów spełniających określone wyrażenie XPath. Jeśli szukany element nie jest pierwszy w modelu DOM, można go znaleźć na podstawie jego indeksu. Przykładowo w formularzu logowania możemy w następujący sposób zlokalizować pole hasła, które jest drugim elementem <input> na stronie.

WebElement password = driver.findElement(By.xpath("//input[2]"));

Wyszukiwanie elementów na podstawie wartości atrybutów w wyrażeniu XPath

Elementy można wyszukiwać na podstawie wartości atrybutów w wyrażeniu XPath. W poniższym przykładzie pole nazwy użytkownika jest identyfikowane za pomocą atrybutu ID.

WebElement userName = driver.findElement(By.xpath("//input[@id='username']"));

Oto kolejny przykład, gdzie zdjęcie zostało zlokalizowane na podstawie atrybutu alt.

WebElement previousButton = driver.findElement(By.xpath("//img[@alt='Poprzedni']"));

Czasami mogą wystąpić sytuacje, kiedy jeden atrybut może nie wystarczyć do zlokalizowania elementu i dokładne dopasowanie wymaga połączenia dodatkowych atrybutów. W poniższym przykładzie wykorzystano wiele atrybutów do zlokalizowania elementu <input> przycisku Zaloguj się.

WebElement previousButton = driver.findElement(By.xpath("//input[@type='submit'][@value='Zaloguj się']"));

Ten sam efekt można osiągnąć za pomocą operatora XPath and.

WebElement previousButton = driver.findElement(By.xpath("//input[@type='submit' and @value='Zaloguj się']"));

W poniższym przykładzie, dzięki wykorzystaniu operatora XPath or do zlokalizowania elementów, użyto dowolnego z wymienionych atrybutów.

WebElement previousButton = driver.findElement(By.xpath("//input[@type='submit'or @value='Zaloguj się']"));

Wyszukiwanie elementów przy użyciu atrybutów z XPath

Ta strategia różni się nieco od wcześniejszej. Tutaj staramy się znaleźć elementy tylko na podstawie specyficznego atrybutu, który został dla nich zdefiniowany, a nie na podstawie wartości atrybutów. Chcemy na przykład wyszukać wszystkie elementy <img>, dla których określono atrybut alt.

List<WebElement> imagesWithAlt = driver.findElements(By.xpath ("//img[@alt]"));

Oto kolejny przykład, w którym zostaną przeszukane wszystkie elementy <img>, gdzie atrybut alt nie został zdefiniowany. Aby sprawdzić warunek negatywny, można użyć funkcji not.

List<WebElement> imagesWithAlt = driver.findElements(By.xpath ("//img[not(@alt)]"));

Funkcje XPath umożliwiają również wyszukiwanie elementów pasujących częściowo do wartości atrybutów. Jest to bardzo przydatne do testowania aplikacji, w których wartości atrybutów są przypisywane dynamicznie i zmieniają się za każdym żądaniem strony. Przykładowo tego rodzaju zachowanie jest typowe dla aplikacji ASP.NET, gdzie identyfikatory są generowane dynamicznie.

Zastosowanie funkcji XPath wyjaśniono w poniższej tabeli.

Składnia

Przykład:

Opis

starts-with()

input[starts-with(@ id,'ctrl')]

Zaczyna się od:

Jeśli na przykład identyfikator ID elementu to ctrl_12, funkcja ta wyszuka i zwróci elementy z ctrl na początku identyfikatora ID.

ends-with()

input[ends-with(@ id,'_userName')]

Kończy się na:

Jeśli na przykład identyfikator elementu to a_1_userName, funkcja wyszuka i zwróci elementy z _userName na końcu identyfikatora.

contains()

Input[contains(@ id,'userName')]

Zawiera:

Jeśli na przykład identyfikator dla elementu to panel_login_userName_textfield, do dopasowania i zlokalizowania elementu funkcja wykorzysta część userName w środku.

Dopasowywanie dowolnego atrybutu na podstawie wartości

XPath dopasowuje atrybut dla wszystkich elementów o podanej wartości i zwraca odpowiedni element. Przykładowo w poniższym zapytaniu XPath podano 'userName'. XPath sprawdzi wszystkie elementy wraz z ich atrybutami, aby dowiedzieć się, czy mają tę wartość, i zwróci pasujący element.

WebElement userName =

    driver.findElement(By.xpath("//input[@*='username']"));

Oto więcej przykładów użycia predykatów XPath do wyszukiwania elementów za pomocą ich położenia i zawartości.

Wyrażenie

Opis

/table/tr[1]

Wybiera pierwszy element tr (wiersz), który jest dzieckiem elementu table.

/table/tr[last()]

Wybiera ostatni element tr (wiersz), który jest dzieckiem elementu table.

/table/tr[last()-1]

Wybiera przedostatni element tr (wiersz), który jest dzieckiem elementu table.

/table/tr[position()>4]

Wybiera trzy elementy tr (wiersze), które są dziećmi elementu table.

//tr[td>40]

Wybiera wszystkie elementy tr (wiersze), dla których jeden z podrzędnych elementów td ma wartość większą niż 40.

Wybieranie nieznanych węzłów

XPath, oprócz wybierania określonych węzłów, dostarcza również symbole wieloznaczne do wybierania grupy elementów.

Symbol

Opis

Przykład:

*

Dopasowuje dowolny węzeł elementu.

/table/*: wybiera wszystkie elementy podrzędne (dzieci) elementu table,

//*: wybiera wszystkie elementy w dokumencie,

//*[@class='price']: wybiera dowolny element w dokumencie, który ma atrybut o nazwie class z określoną wartością — w tym przypadku price.

@

Pasuje do dowolnego węzła atrybutu.

//td[@*]: wybiera wszystkie elementy td, które mają dowolny atrybut.

node()

Pasuje do dowolnego węzła dowolnego rodzaju.

//table/node(): wybiera wszystkie elementy podrzędne (dzieci) elementu table.

Wybieranie wielu ścieżek

Korzystając w wyrażeniach XPath z operatora unii |, można wybrać kilka ścieżek na raz. Przykłady zestawiono w poniższej tabeli.

Wyrażenie ścieżki

Działanie

//div|/p

//div/span

Wybiera wszystkie elementy p (akapity) i span elementu div.

//p | //span

Wybiera wszystkie elementy p (akapity) i span w dokumencie.

Lokalizowanie elementów na podstawie osi XPath

Osie XPath (ang. axes) pomagają znaleźć elementy na podstawie relacji z innymi elementami w dokumencie. Na poniższym zrzucie ekranu pokazano kilka przykładów popularnych osi XPath używanych do wyszukiwania elementów wewnątrz elementu <table>. Można je zastosować do dowolnych innych struktur elementów w aplikacji.

Poniższa ilustracja pokazuje graficzną reprezentację elementów HTML.

Jak to działa?

XPath to rozbudowany język zapytań i przetwarzania drzew modelu DOM w przeglądarkach. Język XPath jest używany do poruszania się pomiędzy elementami i atrybutami w drzewie modelu DOM. XPath zawiera różne reguły, funkcje, operatory i składnię do wyszukiwania elementów.

Oś

Opis

Przykład:

Wynik

ancestor

Wybiera wszystkich przodków bieżącego węzła (rodziców, dziadków i tak dalej).

//td[text()='Produkt 1']/ancestor::table

Wybierze element table.

descendant

Wybiera wszystkich potomków bieżącego węzła (dzieci, wnuki i tak dalej).

/table/descendant::td/input

Wybiera element wejściowy z trzeciej kolumny drugiego wiersza tabeli.

following

Wybiera w dokumencie wszystkie elementy za zamykającym znacznikiem bieżącego węzła.

//td[text()='Produkt 1']/following::tr

Wybiera drugi wiersz elementu table.

followingsibling

Wybiera wszystkie elementy równorzędne (rodzeństwo) za bieżącym węzłem.

//td[text()='Produkt 1']/followingsibling::td

Wybiera drugą kolumnę z drugiego wiersza bezpośrednio za kolumną, której wartość tekstowa to Produkt 1.

preceding

Wybiera wszystkie węzły, które występują w dokumencie przed węzłem bieżącym, z wyjątkiem przodków, węzłów będących atrybutami oraz przestrzeniami nazw.

//td[text()='$150']/preceding::tr

Wybiera wiersz nagłówka.

precedingsibling

Wybiera wszystkie elementy równorzędne przed węzłem bieżącym.

//td[text()='$150']/precedingsibling::td

Wybiera z elementu table pierwszą kolumnę trzeciego wiersza.

Większość przeglądarek obsługuje XPath. Framework Selenium WebDriver zapewnia możliwość wyszukiwania elementów z wykorzystaniem języka XPath.

Za pomocą metody xpath() klasy By można lokalizować elementy przy użyciu składni XPath.

Wyszukiwanie elementów na podstawie selektorów CSS

Arkusze stylów kaskadowych (CSS) to język arkuszy stylów używany do opisywania semantyki prezentacji (wyglądu i formatowania) dokumentu napisanej w języku znaczników, takim jak HTML lub XML.

Główne przeglądarki implementują silniki parsowania CSS pozwalające formatować strony lub nadawać im styl z wykorzystaniem składni CSS. Arkusze te wprowadzono, aby odseparować informacje o sposobie prezentacji od znaczników oraz treści. Więcej informacji na temat CSS i selektorów CSS można znaleźć pod adresem http://en.wikipedia.org/wiki/Cascading_Style_Sheets.

W CSS styl, który ma być zastosowany do elementów modelu DOM, jest zdeterminowany przez reguły dopasowywania wzorców. Dostępne są wzorce, zwane selektorami, o różnym poziomie złożoności — począwszy od prostych nazw elementów, a skończywszy na bogatych wzorcach kontekstowych. Jeśli wszystkie warunki we wzorcu dla określonego elementu są spełnione, selektor pasuje do elementu i przeglądarka stosuje do niego styl zdefiniowany za pomocą składni CSS.

W tej recepturze zaprezentuję kilka prostych selektorów CSS. Następnie przejdę do omawiania selektorów bardziej zaawansowanych.

Jak to zrobić?

Przeanalizujemy niektóre podstawowe selektory CSS, które mogą być używane we frameworku Selenium WebDriver. Klasa By frameworka Selenium WebDriver zawiera metodę cssSelector() służącą do wyszukiwania elementów z wykorzystaniem selektorów CSS.

Wyszukiwanie elementów na podstawie ścieżki bezwzględnej

Ścieżki bezwzględne CSS odwołują się do konkretnej lokalizacji elementu wewnątrz całej hierarchii modelu DOM. Oto przykład, gdzie na podstawie ścieżki bezwzględnej wyszukano pole tekstowe z nazwą użytkownika. Przy podawaniu ścieżki bezwzględnej pomiędzy elementami wprowadza się spacje, tak jak pokazano w poniższym przykładzie kodu.

WebElement userName = driver.findElement(By.cssSelector("html body div div form input"));

Za pomocą separatora > można również użyć poprzedniego selektora, opisując bezpośrednie relacje rodzic-dziecko.

WebElement userName = driver.findElement(By.cssSelector("html > body > div > div > form > input"));

Strategia ta ma jednak ograniczenia, ponieważ zależy od struktury lub hierarchii elementów na stronie. Jeśli to się zmieni, lokalizator nie zdoła znaleźć elementu.

Wyszukiwanie elementów na podstawie ścieżki względnej

Posługując się ścieżką względną, można zlokalizować element bezpośrednio, niezależnie od jego lokalizacji w modelu DOM. Jeśli przykładowo założymy, że pole tekstowe do wprowadzania nazwy użytkownika jest pierwszym elementem <input> w modelu DOM, można je znaleźć w następujący sposób.

WebElement userName = driver.findElement(By.cssSelector("input"));

W opisanych poniżej selektorach CSS do wyszukiwania elementów na podstawie ścieżki względnej wykorzystano atrybuty Class i ID. Jest to sposób analogiczny do korzystania z metod lokalizatorów className() i id(). Istnieje jednak inna strategia, nieobsługiwana przez klasę By, w której możemy użyć dowolnych innych atrybutów elementu.

Wyszukiwanie elementów na podstawie selektora Class

W operacji wyszukiwania elementów na podstawie selektora CSS do zlokalizowania elementu można użyć atrybutu Class. Robimy to, określając typ znacznika HTML, następnie dodając kropkę, a po niej wartość atrybutu class.

WebElement loginButton =

    driver.findElement(By.cssSelector("input.login"));

Wykonanie tej instrukcji spowoduje znalezienie znacznika <input> Zaloguj się, którego atrybut class ma wartość login.

Czasami dla danego elementu podaje się wiele klas CSS. Oto przykład.

<input type="text"

    class="username textfield" />

W tym przypadku można użyć wielu nazw klas, tak jak pokazano w poniższym przykładzie.

WebElement loginButton =

    driver.findElement(By.cssSelector("input.login.textfield"));

Dostępny jest również skrót polegający na podaniu symbolu "." (kropki) oraz wartości atrybutu class i zignorowaniu znacznika HTML. Skorzystanie z tego skrótu zwróci jednak wszystkie elementy z atrybutem class o wartości login; wtedy test może nie zwrócić poprawnego elementu. Przykład zaprezentowano poniżej.

WebElement loginButton = driver.findElement(By.cssSelector(".login"));

Metoda jest podobna do stosowania metody lokalizatora className().

Wyszukiwanie elementów za pomocą selektora ID

Element można znaleźć na podstawie atrybutu id. Zrobimy to, określając typ znacznika HTML, następnie wprowadzając znak # (hash) oraz wartość atrybutu class, tak jak pokazano w poniższym fragmencie kodu.

WebElement userName =

    driver.findElement(By.cssSelector("input#username"));

Wykonanie tej instrukcji zwraca element <input> do wprowadzania nazwy użytkownika na podstawie atrybutu id.

Istnieje również skrót, w którym można wprowadzić # z wartością atrybutu class i zignorować tag HTML. Wtedy jednak otrzymamy wszystkie elementy z identyfikatorem id o wartości username i test może nie zwrócić prawidłowego elementu. Z tej funkcji należy korzystać bardzo ostrożnie.

WebElement userName =

    driver.findElement(By.cssSelector("#username"));

Metoda jest podobna do stosowania strategii lokalizatora id.

Wyszukiwanie elementów za pomocą selektora atrybutu

Selektory CSS, oprócz stosowania atrybutów class i id, pozwalają również wyszukiwać elementy z wykorzystaniem innych atrybutów elementu. W poniższym przykładzie kodu do zlokalizowania elementu <input> użyto atrybutu name.

WebElement userName =

    driver.findElement(By.cssSelector("input[name=username]"));

Korzystanie z atrybutu name do lokalizowania elementów jest podobne do korzystania z metody lokalizatora name() klasy By.

Spróbujmy użyć innych atrybutów do wyszukania elementu. W poniższym przykładzie wyszukano element <img> na podstawie jego atrybutu alt.

WebElement previousButton =

    driver.findElement(By.cssSelector("img[alt='Poprzedni']"));

W niektórych sytuacjach jeden atrybut może nie wystarczyć do znalezienia elementu. W takim przypadku dla dokładnego dopasowania trzeba dołączyć dodatkowe atrybuty. W poniższym przykładzie do zlokalizowania elementu <input> przycisku logowania użyto wielu atrybutów.

WebElement previousButton =

driver.findElement(By.cssSelector("input[type='submit']

[value='Zaloguj się']"));

Wyszukiwanie elementów na podstawie selektora nazwy atrybutu

Strategia ta różni się nieco od wcześniejszej. W tym przypadku chcemy znaleźć elementy tylko na podstawie określonego zdefiniowanego atrybutu, a nie na podstawie wartości atrybutu. Chcemy na przykład wyszukać wszystkie elementy <img>, dla których określono atrybut alt.

List<WebElement> imagesWithAlt =

    driver.findElements(By.cssSelector("img[alt]"));

Można także skorzystać z pseudoklasy not(), aby wyszukać elementy, które nie pasują do podanych kryteriów. Aby na przykład znaleźć wszystkie elementy <img>, które nie mają atrybutu alt, można skorzystać z następującej metody.

List<WebElement> imagesWithoutAlt =

    driver.findElements(By.cssSelector("img:not([alt])"));

Wybieranie wielu ścieżek

Za pomocą selektora or oznaczanego w CSS przez symbol "," można wybrać jeden lub kilka elementów pasujących do podanego kryterium. Oto przykład.

List<WebElement> elements =

    driver.findElements(By.cssSelector("div, p"));

Wykonanie tej instrukcji spowoduje wybranie wszystkich elementów <div> i wszystkich elementów <p>.

List<WebElement> elements =

    driver.findElements(By.cssSelector("div.first, div.last"));

Ta instrukcja wybiera element <div> z klasami first i last.

Dopasowywanie częściowe na podstawie wartości atrybutów

Selektory CSS pozwalają na wyszukiwanie elementów pasujących do częściowych wartości atrybutów. Jest to bardzo przydatne do testowania aplikacji, w których wartości atrybutów są przypisywane dynamicznie i zmieniają się przy każdym żądaniu strony. Przykładowo tego rodzaju zachowanie jest typowe dla aplikacji ASP.NET, w których identyfikatory są generowane dynamicznie. Wykorzystanie składni CSS częściowego dopasowania opisano w poniższej tabeli.

Składnia

Przykład

Opis

^=

input[id^='ctrl']

Zaczyna się od:

Jeśli na przykład identyfikator ID elementu to ctrl_12, ten zapis wyszuka i zwróci elementy z ctrl na początku identyfikatora ID.

$=

input[id$='_userName']

Kończy się na:

Jeśli na przykład identyfikator elementu to a_1_userName, zapis ten wyszuka i zwróci elementy z _userName na końcu identyfikatora.

*=

Input[id*='userName']

Zawiera:

Jeśli na przykład identyfikator dla elementu to panel_login_userName_textfield, do dopasowania i zlokalizowania elementu zostanie wykorzystana część userName w środku.

Jak to działa?

Selektor CSS jest wzorcem i częścią reguły CSS, która pasuje do zbioru elementów w dokumentach HTML lub XML.

Większość przeglądarek obsługuje parsowanie CSS w celu stosowania stylów do elementów. Selenium WebDriver korzysta z silnika parsowania CSS do zlokalizowania elementów na stronie. Selektory CSS zapewniają różne metody, reguły i wzorce do lokalizowania elementów na stronie.

Na podstawie selektora CSS można wyszukiwać w teście elementy na wiele sposobów, za pomocą klasy, identyfikatora, wartości atrybutu i zawartości tekstowej, tak jak opisano w tej recepturze.

Zobacz też

Receptura „Wyszukiwanie elementów z wykorzystaniem zaawansowanych selektorów CSS”.

Lokalizowanie elementów na podstawie tekstu

Podczas testowania aplikacji webowej można napotkać sytuacje, w których programiści nie przypisują żadnych atrybutów do elementów, przez co takie elementy stają się trudne do zlokalizowania.

Korzystając z selektorów CSS lub wyrażeń XPath, można wyszukiwać elementy na podstawie ich zawartości tekstowej. W tej recepturze omówię metody wyszukiwania elementów za pomocą wartości tekstowych.

Jak to zrobić?

Aby znaleźć elementy przy użyciu ich zawartości tekstowej, można użyć selektorów CSS i XPath, które zawierają metody wyszukiwania tekstu wewnątrz elementów. Jeśli element zawiera specyficzny tekst, można użyć tych metod w teście i w ten sposób element znaleźć.

Wykorzystanie funkcji XPath text

XPath udostępnia funkcję text(), którą można wykorzystać do sprawdzenia, czy element zawiera specyficzny tekst. Można to zrobić w następujący sposób.

WebElement cell = driver.findElement

    (By.xpath("//td[contains(text(),'Element 1')]"));

Zamiast funkcji text() można również użyć symbolu kropki . w następujący sposób.

WebElement cell = driver.findElement

    (By.xpath("//td[contains(.,'Element 1')]"));

W pokazanym przykładzie razem z funkcją text() użyliśmy funkcji contains. Funkcja text() zwraca pełny tekst elementu, natomiast funkcja contains() sprawdza występowanie konkretnej wartości.

XPath oferuje również funkcję normalize-space(.), która dopasowuje element na podstawie tekstu tego elementu oraz tekstu elementu potomnego.

Wyszukiwanie elementów w XPath na podstawie dokładnej wartości tekstowej

XPath pozwala wyszukiwać elementy na podstawie dokładnej wartości tekstowej w następujący sposób.

WebElement cell = driver.findElement

    (By.xpath("//td[.='Element 1']"));

Wykonanie tej instrukcji zlokalizuje element <td> pasujący do dokładnego tekstu elementu.

Wykorzystanie pseudoklasy contains() selektorów CSS

Selektory CSS udostępniają pseudoklasę contains(), którą można wykorzystać do sprawdzenia, czy element zawiera specyficzny tekst. Aby na przykład w teście znaleźć komórki tabeli na podstawie ich treści, można to zrobić w następujący sposób.

WebElement cell =

    driver.findElement(By.cssSelector("td:contains('Element 1')"));

Pseudoklasa contains() pobiera parametr określający tekst, który ma być przeszukiwany. Następnie sprawdza w modelu DOM wszystkie elementy <td> w poszukiwaniu podanego tekstu.

Pseudoklasa contains() może nie działać z przeglądarkami, które natywnie nie obsługują selektorów CSS. Ponadto została wyeliminowana ze specyfikacji CSS3.

Alternatywą dla pseudoklasy contains() może być atrybut innerText (nie działa z przeglądarką Firefox) lub atrybut textContent (dla przeglądarki Firefox). Można z nich korzystać w następujący sposób.

WebElement cell =

    driver.findElement(By.cssSelector("td[innerText='Item 1']"));

Albo tak.

WebElement cell = driver.findElement

    (By.cssSelector("td[textContent='Element 1']"));

Można również użyć selektorów jQuery, które obsługują pseudoklasę contains().

Jak to działa?

Selektory CSS i XPath udostępniają metody, za pomocą których można wyszukiwać elementy na podstawie ich zawartości tekstowej. Metoda przydaje się, gdy elementy nie mają wystarczająco dużo atrybutów lub gdy nie działają inne strategie wyszukiwania.

Podczas wyszukiwania elementów na podstawie ich zawartości tekstowej zarówno selektory CSS, jak i wyrażenia XPath przeszukują model DOM w poszukiwaniu elementów zawierających specyficzną wartość tekstową i zwracają pasujące elementy.

Wyszukiwanie elementów na podstawie zaawansowanych selektorów CSS

W poprzednich recepturach zaprezentowałem kilka podstawowych selektorów CSS. W tej recepturze opiszę kilka zaawansowanych selektorów CSS do wyszukiwania elementów.

Jak to zrobić?

W recepturze „Wyszukiwanie elementów na podstawie selektorów CSS” badaliśmy niektóre podstawowe selektory CSS. Teraz przyjrzymy się zaawansowanym selektorom CSS, takim jak kombinatory rodzeństwa i pseudoklasy. Omówiłem je w poniższych podpunktach.

Wyszukiwanie potomków

Selektory CSS zapewniają różne sposoby wyszukiwania elementów potomków na podstawie rodziców.

Aby na przykład znaleźć pole Username do wprowadzania nazwy użytkownika w polu formularza logowania, można skorzystać z selektora opisanego poniżej. W tym przykładzie operator > zastosowano do oznaczenia relacji rodzic-dziecko.

WebElement userName =

    driver.findElement(By.cssSelector("form#loginForm > input"));

W podobny sposób można wykorzystać metodę nth-child().

WebElement userName = driver.findElement

    (By.cssSelector("form#loginForm :nth-child(2)"));

W tym przypadku pole Username jest drugim elementem formularza <form>. W tabeli zamieszczonej poniżej zaprezentowano kilka strukturalnych pseudoklas służących do wyszukiwania elementów potomnych.

Pseudoklasa

Przykład

Opis

:first-child

form#loginForm :first-child

Wyszuka pierwszy element potomny elementu form. Jest nim etykieta pola username.

:last-child

form#loginForm :last-child

Wyszuka ostatni element wewnątrz elementu form. Jest nim przycisk logowania (Zaloguj się).

:nth-child(2)

form#loginForm :nth-child(2)

Wyszuka drugi element potomny elementu form — to znaczy pole Username.

Wyszukiwanie elementów równorzędnych

Za pomocą selektorów CSS i operatora + możemy szukać elementów równorzędnych. I tak element <p> z opisem Opis produktu 2 na przykładowej stronie można wyszukać w następujący sposób.

WebElement productDescription =

    driver.findElement(By.cssSelector("div#top5 > p + p"));

W tym przykładzie pierwszym potomkiem elementu div#top5 będzie element <p> z opisem Opis produktu 1, a jego bezpośrednim rodzeństwem będzie element <p> z opisem Opis produktu 2. W poniższej tabeli zestawiono kilka dodatkowych kombinacji do wyszukiwania rodzeństwa.

p + p

div#top5 > p + p

Element-rodzeństwo występujący bezpośrednio po. Zastosowanie tego wyrażenia wyszuka etykietę Opis produktu 2.

p + * + p

div#top5 > p + * + p

Następny element z rodzeństwa oddzielony jednym elementem pośrednim. Zastosowanie tego wyrażenia wyszuka etykietę Opis produktu 3.

Wykorzystanie pseudoklas opisujących działania użytkownika

Przy użyciu akcji użytkownika opisywanych pseudoklasą :focus można znaleźć element, który ma aktualnie fokus.

WebElement productDescription =

    driver.findElement(By.cssSelector("input:focus"));

Skorzystanie z tego wyrażenia pozwala zlokalizować dowolny element, który aktualnie ma fokus wprowadzania. Można także wyszukiwać elementy z pomocą pseudoklas :hover i :active.

Wykorzystanie pseudoklas opisujących stan interfejsu użytkownika

Przy użyciu pseudoklas opisujących stan interfejsu użytkownika można wyszukać elementy w różnych stanach. Można na przykład wyszukać kontrolki, które są aktywne, nieaktywne albo zaznaczone. Szczegóły opisano w poniższej tabeli.

Pseudoklasa

Przykład

Opis

:enabled

input:enabled

Wyszukuje wszystkie elementy, które są aktywne do wprowadzania danych wejściowych użytkownika.

:disabled

input:enabled

Wyszukuje wszystkie elementy, które są wyłączone dla wprowadzania danych wejściowych użytkownika.

:checked

input:checked

Wyszukuje wszystkie elementy (pola wyboru), które są zaznaczone.

Jak to działa?

Do wyszukiwania elementów we frameworku Selenium WebDriver API, oprócz podstawowych selektorów CSS, można również użyć różnych zaawansowanych metod selektorów CSS, takich jak pseudoklasy CSS lub kombinatory elementów sąsiednich.

Obszerną listę selektorów CSS wraz ze sposobami ich użycia można znaleźć pod adresem http://www.w3schools.com/cssref/css_selectors.asp.

Zobacz też

Receptura „Wyszukiwanie elementów na podstawie selektorów CSS”.

Wykorzystanie selektorów jQuery

Selektory jQuery są ważnymi funkcjami bibliotek jQuery. Podstawę stanowią selektory CSS1-3 oraz kilka selektorów dodatkowych. Dla tych selektorów użyto znajomej składni CSS Selector. Dzięki niej programiści mogą szybko i łatwo zidentyfikować elementy na stronie przy użyciu metod biblioteki jQuery. Selektory te, podobne do selektorów CSS, pozwalają znaleźć elementy HTML i manipulować nimi. Mogą zwracać zarówno pojedyncze elementy, jak i ich listę.

Selektory jQuery można wykorzystywać tam, gdzie przeglądarki nie obsługują natywnie selektorów CSS.

W tej recepturze pokrótce pokażę, w jaki sposób można użyć selektorów jQuery razem z frameworkiem Selenium WebDriver.

Jak to zrobić?

Utwórzmy test, który sprawdza, czy w czasie, gdy wyświetlana jest strona, zaznaczone są wybrane pola wyboru.

@Test

    Public void testDefaultSelectedCheckbox() {}

        // Lista oczekiwanych zaznaczonych pól wyboru

        List<String> checked = Arrays

           .asList("user128_admin", "user220_browser");

        // Utworzenie egzemplarza obiektu JavascriptExecutor z poziomu sterownika

        JavascriptExecutor js = (JavascriptExecutor) driver;

        // Zlokalizowanie wszystkich zaznaczonych pól wyboru poprzez wywołanie metody jQuery

find()

        // Metoda find() zwraca elementy w tablicy

    @SuppressWarnings("unchecked")

    List<WebElement> elements = (List<WebElement>) js

        .executeScript("return jQuery.find(':checked')");

    // Sprawdzenie, czy zaznaczone są dwa pola wyboru

    assertEquals(elements.size(), 2);

    // Sprawdzenie, czy są zaznaczone odpowiednie pola wyboru

    for (WebElement element : elements) {

        assertTrue(checked.contains(element.getAttribute("id")));

    }

}

Jak to działa?

Framework Selenium WebDriver może być wzbogacony selektorami jQuery dostępnymi za pośrednictwem interfejsu jQuery API. Przed skorzystaniem z tych selektorów trzeba się jednak upewnić, czy na stronie załadowano jQuery API. Interfejs JQuery API zawiera funkcję find(), za pomocą której można wyszukiwać elementy. Aby skorzystać z metody find() jQuery, trzeba użyć klasy JavascriptExecutor. W poniższym przykładzie wyszukamy wszystkie zaznaczone pola wyboru na stronie poprzez wywołanie metody find().

// Zlokalizowanie wszystkich zaznaczonych pól wyboru poprzez wywołanie metody jQuery find().

// Metoda find() zwraca elementy w tablicy

List<WebElement> elements = (List<WebElement>)

    js.executeScript("return jQuery.find(':checked')");

Metoda find() zwraca pojedynczy obiekt WebElement lub listę obiektów WebElement spełniających kryteria określone za pomocą selektora. Więcej szczegółów oraz listę selektorów jQuery można znaleźć na stronie http://api.jquery.com/category/selectors/.

Metoda find() biblioteki jQuery pozwala również na używanie opisanych w tym rozdziale selektorów CSS.

Co dalej?

Aby można było skorzystać z selektorów jQuery, testowana strona musi załadować bibliotekę jQuery. Jeśli aplikacja nie używa jQuery, można załadować bibliotekę jQuery na stronie poprzez dołączenie biblioteki jQuery w czasie wykonywania aplikacji, tak jak pokazano poniżej.

private void injectjQueryIfNeeded() {

    if (!jQueryLoaded())

        injectjQuery();

}

public Boolean jQueryLoaded() {

    Boolean loaded;

    try {

        loaded = (Boolean) driver.executeScript("return

            jQuery()!=null");

    } catch (WebDriverException e) {

        loaded = false;

    }

    return loaded;

}

public void injectjQuery() {

    driver.executeScript(" var headID =

        document.getElementsByTagName(\"head\")[0];"

    + "var newScript = document.createElement('script');"

    + "newScript.type = 'text/javascript';"

    + "newScript.src = 'http://ajax.googleapis.com/

        ajax/libs/jquery/1.7.2/jquery.min.js';"

    + "headID.appendChild(newScript);");

}

Metoda injectjQueryIfNeeded() wywołuje wewnętrznie metodę jQueryLoaded(), aby sprawdzić, czy na stronie jest dostępny obiekt jQuery. Jeśli strona nie ma zdefiniowanego obiektu jQuery, metoda injectjQueryIfNeeded() wywołuje metodę injectjQuery() w celu dołączenia biblioteki jQuery do nagłówka strony w czasie wykonywania aplikacji. Jest to realizowane poprzez dodanie na stronie elementu <script> odwołującego się do repozytorium Google Content Delivery Network (CDN) w celu pobrania pliku biblioteki jQuery. Możesz zmienić wersję używaną w tym przykładzie na najnowszą wersję biblioteki jQuery.

Rozdział 3.
Obsługa elementów HTML

W tym rozdziale omówię różne metody interakcji z elementami HTML wyświetlanymi na stronie oferowane przez framework Selenium WebDriver. Oto receptury, jakie zostaną opisane.

Automatyzowanie pól tekstowych, obszarów tekstowych i przycisków.

Sprawdzanie tekstu elementu.

Sprawdzanie atrybutów elementów i wartości CSS.

Automatyzowanie rozwijanych menu i list.

Zaznaczanie opcji na rozwijanych menu i listach.

Zaznaczanie wybranych opcji na rozwijanych menu i listach.

Automatyzacja przełączników i grup opcji.

Automatyzowanie pól wyboru.

Praca z obiektami WebTable.

Wprowadzenie

Selenium WebDriver dostarcza bardzo obszerne API do pracy z różnymi typami elementów webowych, do interakcji z interfejsem użytkownika, do wykonywania kodu JavaScript, a także obsługuje różne rodzaje kontrolek, takie jak listy, rozwijane menu, przełączniki i pola wyboru.

W tym rozdziale omówię sposób wykorzystania tych własności do tworzenia różnych kroków testowych — począwszy od prostych, a skończywszy na złożonych. Lektura tego rozdziału pomoże również pokonać kilka typowych problemów napotykanych podczas tworzenia zautomatyzowanych skryptów testowych z wykorzystaniem frameworka Selenium WebDriver. Przykłady zamieszczone w rozdziale utworzono z zastosowaniem dowiązań frameworka Selenium WebDriver dla Javy. Przykładowy kod dołączony do tego rozdziału zawiera niektóre receptury zaimplementowane w językach C#, Ruby i Python.

Automatyzowanie pól tekstowych, obszarów tekstowych i przycisków

Pola tekstowe i przyciski to najbardziej typowe elementy używane w aplikacjach webowych. Interfejs WebElement frameworka Selenium WebDriver udostępnia metody do symulowania wejścia z klawiatury w polach lub obszarach tekstowych oraz wykonywania kliknięć kontrolek przycisków.

W tej recepturze opiszę wykorzystanie tych metod do automatyzacji pól tekstowych, obszarów tekstowych i przycisków.

Jak to zrobić?

W recepturze omówione zostaną metody clear(), sendKeys(), submit() i click() interfejsu WebElement.

Usuwanie tekstu z pól i obszarów tekstowych

Do usuwania istniejącej wartości tekstowej z pola tekstowego lub obszaru tekstowego można użyć metody clear(), tak jak pokazano w poniższym przykładzie.

// Znajdź element do wprowadzania tekstu na podstawie jego nazwy

WebElement element = driver.findElement(By.name("q"));

// Wyczyść istniejące wartości tekstowe za pomocą metody clear()

element.clear();

Wprowadzanie tekstu w polach i obszarach tekstowych

Aby wprowadzić wartość tekstową do pola lub obszaru tekstowego, można użyć metody sendKeys(), tak jak pokazano w poniższym przykładzie.

// Wpisz informacje do wyszukania

element.sendKeys("Selenium i testowanie aplikacji. Receptury");

Przesyłanie formularzy

Formularze HTML do wprowadzania danych standardowo tworzy się za pomocą elementu <form>. Kiedy użytkownik wprowadzi dane i zapisze formularz, jest on przekazywany na zdalny serwer w celu przetwarzania. Oto przykład przesłania formularza wyszukiwania Google przy użyciu wywołania metody submit() pola tekstowego wyszukiwania na stronie głównej Google.

// Znajdź element do wprowadzania tekstu na podstawie jego nazwy

WebElement element = driver.findElement(By.name("q"));

// Wyczyść tekst zapisany w elemencie

element.clear();

// Wpisz informacje do wyszukania

element.sendKeys("Selenium i testowanie aplikacji. Receptury");

// Teraz prześlij formularz. WebDriver znajdzie go na podstawie elementu

element.submit();

Wykonywanie kliknięć przycisku

Do kliknięcia przycisku można wykorzystać metodę click() interfejsu WebElement.

// Znajdź element przycisku na podstawie jego nazwy

WebElement element = driver.findElement(By.name("btnG"));

// Kliknij przycisk

element.click();

Jak to działa?

Metoda clear() interfejsu WebElement działa tylko na elementach <input> reprezentujących pola i obszary tekstowe. Nie ma wpływu na inne typy elementów. Jej wywołanie powoduje wyczyszczenie elementu z wszystkich wcześniejszych wartości.

Metodę sendKeys() można zastosować w odniesieniu do dowolnego elementu, który akceptuje wartości. Jej działanie polega na pisaniu w elemencie. Metoda ta symuluje wpisywanie do elementu i ustawia jako wartość tego elementu podany ciąg. Metoda sendKeys() pobiera wartość java.lang.CharSequence lub łańcuch znaków.

Można także symulować naciskanie klawiszy, które nie reprezentują tekstu. Do tego celu można wykorzystać metodę sendKeys() i przekazać do niej wartości typu wyliczeniowego Keys. Przykładowo po wpisaniu wartości w polu tekstowym możemy nacisnąć klawisz TAB, tak jak pokazano w poniższym fragmencie kodu.

element.sendKeys ("123" + Keys.TAB);

Pełną listę dostępnych klawiszy można znaleźć pod adresem https://seleniumhq.github.io/selenium/docs/api/java/org/openqa/selenium/Keys.html.

Metodę submit() można zastosować w odniesieniu do elementu <form> lub dowolnego elementu, który jest jego potomkiem. Programiści mogą tworzyć przyciski lub łącza do przesyłania formularzy. Gdy użytkownik kliknie element przesłania formularza, zostaje wyzwolone zdarzenie onsubmit. W Selenium WebDriver to zdarzenie może być wywołane przez każdy element będący częścią formularza lub element w obrębie elementu <form>. Jest ono dostępne za pomocą metody submit() interfejsu WebElement.

Metoda click() powoduje kliknięcie elementu. Jeśli to kliknięcie powoduje zmianę bieżącej strony wyświetlanej w przeglądarce, skrypt będzie czekać do momentu załadowania nowej strony. Jeśli jednak spowoduje załadowanie nowej strony za pośrednictwem zdarzenia lub poprzez przesłanie zdarzenia natywnego, metoda ta nie będzie oczekiwać na załadowanie strony. W takich przypadkach trzeba sprawdzić, czy nowa strona została załadowana.

Istnieją pewne warunki wstępne, których spełnienie jest wymagane do tego, by można było kliknąć element za pomocą metody click(). Element musi być widoczny, musi też mieć wysokość i szerokość większą niż 0. Próba kliknięcia niewidocznego elementu powoduje zgłoszenie wyjątku ElementNotVisibleException.

Zobacz też

Receptura „Sprawdzanie tekstu elementu”,

Receptura „Sprawdzanie atrybutów elementu i wartości CSS”.

Sprawdzanie tekstu elementu

Podczas testowania aplikacji webowej trzeba sprawdzić, czy elementy wyświetlają odpowiednie wartości lub właściwy tekst na stronie. Interfejs WebElement frameworka Selenium WebDriver udostępnia metody do pobierania i sprawdzania tekstu elementu. Czasami trzeba pobrać tekst lub wartości z elementu do zmiennej w czasie wykonywania aplikacji i później użyć ich w innym miejscu w przebiegu testu.

W tej recepturze pobiorę i sprawdzę tekst elementu za pomocą metody getText() interfejsu WebElement.

Jak to zrobić?

Utwórzmy test, który znajduje element, a następnie pobiera z niego tekst do zmiennej łańcuchowej. Poprawność tego ciągu sprawdzimy za pomocą następującego kodu.

@Test

Public void testElementText() {}

    // Pobranie wartości elementu message

    WebElement message = driver.findElement(By.id("message"));

    // Pobranie wartości tekstowej elementu message

    String messageText = message.getText();

    // Sprawdzenie, czy w elemencie message wyświetlany jest komunikat „Kliknij mnie, aby

    // zmienić kolor”

    assertEquals("Kliknij mnie, aby zmienić kolor", messageText);

    // Pobranie elementu area

    WebElement area = driver.findElement(By.id("area"));

    // Sprawdzenie, czy tekst elementu area wyświetla tekst "Tekst div/nTekst span"

    assertEquals("Tekst div\nTekst span", area.getText());

}

Jak to działa?

Metoda getText() interfejsu WebElement zwraca widoczną właściwość innerText elementu włącznie z elementami potomnymi, bez wiodących i końcowych spacji.

Jeśli element ma elementy potomne, to razem z atrybutem innerText elementu-rodzica zostaną zwrócone wartości atrybutów innerText elementów-dzieci. W poniższym przykładzie element <span> jest osadzony wewnątrz elementu <div>. Podczas pobierania wartości innerText elementu <div> dołączany jest również element innerText elementu <span>.

// Pobranie elementu area

WebElement area = driver.findElement(By.id("area"));

// Sprawdzenie, czy tekst elementu wyświetla "Tekst div\nTekst span"

assertEquals("Tekst div\nTekst span",area.getText());

Co dalej?

Metoda WebElement.getText() nie działa z elementami ukrytymi lub niewidocznymi. Pobieranie tekstu z takich elementów jest możliwe za pośrednictwem metody getAttribute() z wykorzystaniem atrybutów innerText lub textContent.

assertEquals("Tekst div\nTekst span",area.GetAttribute("innerText"));

Atrybut innerText nie jest obsługiwany w przeglądarce Firefox, natomiast atrybut textContent nie jest obsługiwany w przeglądarce Internet Explorer.

Zobacz też

Receptura „Sprawdzanie atrybutów elementu i wartości CSS”.

Sprawdzanie atrybutów elementu i wartości CSS

W celu zarządzania zachowaniem lub stylem elementów podczas ich wyświetlania w przeglądarce programiści konfigurują różne atrybuty elementów wyświetlanych na stronie WWW. Mogą to robić zarówno w fazie projektowania, jak i w czasie działania aplikacji. Przykładowo przez ustawienie atrybutu readonly element <input> można skonfigurować jako tylko do odczytu.

Atrybut elementu można pobrać i sprawdzić za pomocą metody getAttribute() interfejsu WebElement.

Dla elementów wyświetlanych w aplikacji webowej można stosować różne style. Dzięki temu wyglądają one estetyczniej i stają się bardziej użyteczne. Programiści nadają te style za pomocą kaskadowych arkuszy stylów (ang. Cascading Style Sheets — CSS). Wartość CSS można odczytać przy użyciu metody getCSSValue() klasy WebElement.

W tej recepturze skorzystam z funkcji getCSSValue() w celu sprawdzenia stylu atrybutu zdefiniowanego dla elementu.

Wartość atrybutu elementu sprawdzę za pomocą metody getAttribute().

Jak to zrobić?

Do utworzenia testu, który lokalizuje element i sprawdza wartość jego atrybutu, może służyć poniższy kod.

@Test

public void testElementAttribute() {

    WebElement message = driver.findElement(By.id("message"));

    assertEquals("justify", message.getAttribute("align"));

}

Utwórzmy test, który odczytuje atrybut CSS width i weryfikuje uzyskaną wartość za pomocą następującego kodu.

@Test

public void testElementStyle() {

    WebElement message = driver.findElement(By.id("message"));

    String width = message.getCssValue("width");

    assertEquals("150px", width);

}

Jak to działa?

Jeśli do metody getAttribute() przekażemy nazwę atrybutu, w odpowiedzi do testu zostanie zwrócona wartość atrybutu. W tym przykładzie sprawdzamy, czy atrybut align elementu <p> został ustawiony na justify.

assertEquals("justify", message.getAttribute("align"));

Jeśli do metody getCSSValue() przekażemy nazwę atrybutu CSS, w odpowiedzi zostanie zwrócona wartość atrybutu CSS. W tym przykładzie sprawdzamy, czy atrybut width elementu <div> został ustawiony na 150px.

String width = message.getCssValue("width");

assertEquals("150px", width);

Automatyzowanie rozwijanych menu i list

Selenium WebDriver za pomocą specjalnej klasy Select obsługuje testowanie elementów menu i list rozwijanych.

Klasa Select dostarcza metody i właściwości do interakcji z menu i listami rozwijanymi utworzonymi za pomocą elementu HTML <select>.

W tej recepturze zautomatyzuję kontrolki menu i list rozwijanych za pośrednictwem klasy Select.

Jak to zrobić?

Utwórzmy test dla kontrolki menu rozwijanego. Test wykona kilka podstawowych sprawdzeń, a następnie wywoła różne metody zaznaczające opcje w rozwijanym menu.

@Test

Public void testDropdown() {}

    // Pobranie menu rozwijanego jako obiektu Select na podstawie atrybutu name

    Select make = new Select(driver.findElement(By.name("make")));

    // Sprawdzenie, czy menu rozwijane obsługuje wybór wielokrotny

    assertFalse(make.isMultiple());

    // Sprawdzenie, czy menu rozwijane ma cztery opcje wyboru

    assertEquals (4, make.getOptions().size());

    // Klasa Select pozwala zaznaczyć opcję menu rozwijanego na podstawie widocznego tekstu

    make.selectByVisibleText("Honda");

    assertEquals("Honda", make.getFirstSelectedOption().getText());

    // Można także zaznaczyć opcję w menu rozwijanym przy użyciu wartości atrybutu

    make.selectByValue("audi");

    assertEquals("Audi", make.getFirstSelectedOption().getText());

    // Opcję w menu rozwijanym można też wybrać przy użyciu indeksu

    make.selectByIndex(0);

    assertEquals("BMW", make.getFirstSelectedOption().getText());

}

Spróbujmy utworzyć inny test dla kontrolki listy — tym razem z włączoną opcją wielokrotnego wyboru. Test wykona kilka podstawowych sprawdzeń, a następnie wywoła metody, które zaznaczają na liście wiele opcji. Test sprawdzi wybrane opcje, po czym usunie zaznaczenie poprzez wywołanie różnych metod anulowania zaznaczenia — odpowiednio — na podstawie widocznego tekstu, wartości i indeksu, tak jak pokazano w poniższym przykładowym kodu.

@Test

Public void testMultipleSelectList() {}

    // Pobranie listy jako obiektu Select na podstawie atrybutu name

    Select color = new Select(driver.findElement(By.name("color")));

    // Sprawdzenie, czy lista pozwala na wybór wielokrotny

    assertTrue(color.isMultiple());

    // Sprawdzenie, czy lista zawiera pięć opcji do wyboru

    assertEquals(5, color.getOptions().size());

    // Zaznaczenie wielu opcji na liście na podstawie widocznego tekstu

    color.selectByVisibleText("Czarny");

    color.selectByVisibleText("Czerwony");

    color.selectByVisibleText("Srebrny");

    // Anulowanie zaznaczenia opcji na podstawie atrybutu value

    color.deselectByValue("rd");

    // Sprawdzenie liczby zaznaczonych opcji

    assertEquals(1, color.getAllSelectedOptions().size());

    // Anulowanie zaznaczenia opcji przy użyciu indeksu opcji

    color.deselectByIndex(0);

    // Sprawdzenie liczby zaznaczonych opcji

    assertEquals(0, color.getAllSelectedOptions().size());

}

Jak to działa?

Elementy reprezentujące rozwijane menu lub listy można znaleźć podobnie jak inne elementy na stronie. Jednak do interakcji z tymi elementami wykorzystujemy klasę Select. Można to zrobić w następujący sposób.

Select color = new Select(driver.findElement(By.name("color")));

Element HTML <select> obsługuje rozwijane menu lub listy z opcją wielokrotnego wyboru. Do sprawdzenia, czy element obsługuje wybór wielokrotny, można wykorzystać metodę isMultiple() klasy Select, tak jak pokazano poniżej. Metoda zwraca true, jeśli kontrolka obsługuje wybór wielokrotny, i false, jeśli tak nie jest.

assertFalse(make.isMultiple());

Aby sprawdzić liczbę dostępnych opcji w rozwijanym menu lub na liście, można wywołać metodę getOptions() klasy Select, a następnie zbadać rozmiar zwróconej kolekcji obiektów WebElement.

assertEquals(4, make.getOptions().size());

Klasa Select zapewnia trzy różne sposoby zaznaczania i anulowania zaznaczenia opcji rozwijanych menu i list, co pokazano w poniższych podpunktach.

Zaznaczanie (anulowanie zaznaczenia) na podstawie widocznego tekstu

Opcję możemy zaznaczyć na podstawie jej widocznego tekstu. Przyjrzyjmy się fragmentowi kodu dla elementu <select>.

<select name="color" size="6" multiple="multiple"

    style="width:100px">

    <option value="bl">Czarny</option>

    <option value="wt">Biały</option>

    <option value="rd">Czerwony</option>

    <option value="br">Brązowy</option>

    <option value="sl">Srebrny</option>

</select>

Każda opcja w tym elemencie <select> ma właściwość value, a także etykietę tekstową określoną pomiędzy tagami <option> i </option>. Na podstawie tej etykiety tekstowej można zaznaczyć opcję przy użyciu wywołania metody selectByVisibleText() klasy Select, tak jak pokazano w poniższym kodzie.

color.selectByVisibleText("Czarny");

W podobny sposób można anulować zaznaczenie opcji poprzez wywołanie metody deselectByVisibleText(), tak jak pokazano poniżej.

color.deselectByVisibleText("Czarny");

Zaznaczanie (anulowanie zaznaczenia) na podstawie wartości

Opcję można również zaznaczyć na podstawie jej atrybutu value. Przykładowo poniższa opcja ma atrybut value "bl" i etykietę tekstową "Czarny".

<option value="bl">Czarny</option>

Aby zaznaczyć tę opcję na podstawie wartości, należy wywołać metodę selectByValue() klasy Select, tak jak pokazano w następującym wierszu kodu.

color.selectByValue("bl");

W podobny sposób można anulować zaznaczenie wcześniej zaznaczonej opcji poprzez wywołanie metody deselectByVisibleText(), tak jak pokazano w poniższym kodzie.

color.deselectByValue("bl");

Zaznaczanie (anulowanie zaznaczenia) na podstawie indeksu

Istnieje jeszcze jeden sposób zaznaczania opcji — na podstawie jej indeksu. Gdy opcje są wyświetlane na stronie, są indeksowane w kolejności, w jakiej zostały na tej stronie zdefiniowane. Możemy wywołać metodę selectByIndex() klasy Select, określając wartość indeksu, tak jak pokazano w poniższym kodzie.

color.selectByIndex("0");

W podobny sposób można usunąć zaznaczenie wcześniej zaznaczonej opcji poprzez wywołanie metody deselectByIndex(). Kompletną składnię pokazano poniżej.

color.deselectByIndex(0);

Metoda może spowodować problemy, w przypadku gdy opcje są generowane dynamiczne, a ich indeks często się zmienia.

Co dalej?

Możemy zaznaczyć (anulować zaznaczenie) wiele opcji z rozwijanego menu (rozwijanej listy), wywołując metody zaznaczania (anulowania zaznaczenia) kilka razy po kolei. Przykładowo na liście kolorów, która obsługuje wybór wielokrotny, możemy zaznaczyć opcje w następujący sposób.

color.selectByVisibleText("Czarny");

color.selectByVisibleText("Czerwony");

color.selectByVisibleText("Srebrny");

Spowoduje to zaznaczenie na liście opcji Czarny, Czerwony i Srebrny.

Zobacz też

Receptura „Zaznaczanie wybranych opcji na rozwijanych menu i listach”.

Zaznaczanie opcji w elementach Select

Podczas testowania menu i list rozwijanych utworzonych za pomocą elementu <select> czasami trzeba sprawdzić, czy zostały wyświetlone właściwe opcje do wyboru. Mogą to być opcje statyczne lub opcje wypełnione z bazy danych za pośrednictwem wywołań AJAX.

W tej recepturze pokażę, w jaki sposób sprawdzać opcje z wartościami oczekiwanymi.

Przygotuj się

W tej recepturze będzie potrzebny test utworzony we wcześniejszej recepturze „Automatyzowanie rozwijanych menu i list”. W celu sprawdzenia opcji wprowadzimy dodatkowe kroki.

Jak to zrobić?

Spróbujmy zmodyfikować metodę testDropdown() pod kątem sprawdzania opcji. Dodaj do testu następujący kod:

@Test

Public void testDropdown() {}

    // Pobranie menu rozwijanego jako obiektu Select na podstawie atrybutu name

    Select make = new Select(driver.findElement(By.name("make")));

    // Sprawdzenie, czy menu rozwijane obsługuje wybór wielokrotny

    assertFalse(make.isMultiple());

    // Sprawdzenie, czy menu rozwijane ma cztery opcje wyboru

    assertEquals(4, make.getOptions().size());

    // Sprawdzenie, czy w rozwijanym menu są oczekiwane wartości zgodne z elementami tablicy

    List<String> expectedOptions = Arrays.asList("BMW", "Mercedes","Audi", "Honda");

    List<String> actualOptions = new ArrayList<String>();

    // Pobranie wartości opcji rozwijanego menu za pomocą metody getOptions()

    for (WebElement option : make.getOptions()) {

        actualOptions.add(option.getText());

    }

    // Sprawdzenie, czy elementy tablicy opcji oczekiwanych i rzeczywistych do siebie pasują

    assertArrayEquals(expectedOptions.toArray(), actualOptions.toArray());

    // Klasa Select pozwala zaznaczyć opcję menu rozwijanego na podstawie widocznego tekstu

    make.selectByVisibleText("Honda");

    assertEquals("Honda", make.getFirstSelectedOption().getText());

    // Można także zaznaczyć opcję w menu rozwijanym przy użyciu wartości atrybutu

    make.selectByValue("audi");

    assertEquals("Audi", make.getFirstSelectedOption().getText());

    // Opcję w menu rozwijanym można też wybrać przy użyciu indeksu

    make.selectByIndex(0);

    assertEquals("BMW", make.getFirstSelectedOption().getText());

}

Jak to działa?

Wybieranie opcji w rozwijanym menu lub na liście wymaga zastosowania nieco innego podejścia, ponieważ w klasie Select nie istnieje wbudowana metoda służąca do tego celu. W tym podejściu utworzymy listę oczekiwanych wartości, które chcemy zaznaczyć w menu lub na liście, tak jak pokazano w poniższym kodzie.

List<String> expectedOptions = Arrays.asList("BMW","Mercedes","Audi","Honda");

Tekstowe etykiety dla wszystkich opcji będą umieszczane na podobnej liście. W tym celu będziemy iterować po wszystkich opcjach za pomocą metody getOptions() klasy Select. Metoda getOptions() zwraca listę wszystkich opcji jako egzemplarzy klasy WebElement. Za pomocą metody getText() klasy WebElement do tablicy actualOptions zostaną dodane tekstowe etykiety wszystkich opcji.

List<String> act_options = new ArrayList<String>();

    // Pobranie wartości opcji rozwijanego menu za pomocą metody getOptions()

    for(WebElement option : make.getOptions()) {

actualOptions.add(option.getText()); }

Na koniec porównamy listę expectedOptions z listą actualOptions w celu znalezienia niedopasowania.

assertArrayEquals(expectedOptions.toArray(),actualOptions.toArray());

Co dalej?

Aby sprawdzić, czy jest do wyboru określona opcja, możemy po prostu sprawdzić listę elementów tablicy actualOptions w następujący sposób.

assertTrue(actualOptions.contains("BMW"));

Zobacz też

Receptura „Automatyzowanie menu rozwijanych i list”,

Receptura „Zaznaczanie wybranych opcji na rozwijanych menu i listach”.

Zaznaczanie wybranych opcji na rozwijanych menu i listach

W poprzednich recepturach pokazałem, jak wybierać opcje z rozwijanych menu i list, a także jak sprawdzić, jakie opcje są dostępne do wyboru. Trzeba także sprawdzić, czy w tych kontrolkach zostały zaznaczone odpowiednie opcje — albo domyślnie, albo przez użytkownika.

W tej recepturze pokażę, w jaki sposób sprawdzić, czy w rozwijanym menu lub na liście zaznaczono odpowiednie opcje.

Przygotuj się

W tej recepturze będzie potrzebny test utworzony we wcześniejszej recepturze „Automatyzowanie rozwijanych menu i list”.

W celu sprawdzenia opcji wykonamy dodatkowe czynności.

Jak to zrobić?

Spróbujmy zmodyfikować metodę testDropdown() pod kątem sprawdzania opcji. Dodaj do testu kod zaznaczony wyróżnieniem.

@Test

Public void testDropdown()

{

    ...

    // Klasa Select pozwala zaznaczyć opcję menu rozwijanego na podstawie widocznego tekstu

    make.selectByVisibleText("Honda");

    assertEquals("Honda", make.getFirstSelectedOption().getText());

    // Można także zaznaczyć opcję w menu rozwijanym przy użyciu wartości atrybutu

    make.selectByValue("audi");

    assertEquals("Audi", make.getFirstSelectedOption().getText());

    // Opcję w menu rozwijanym można też wybrać przy użyciu indeksu

    make.selectByIndex(0);

    assertEquals("BMW", make.getFirstSelectedOption().getText());

}

Zmodyfikujemy także metodę testMultipleSelectList(). Dodaj do testu kod oznaczony wyróżnieniem.

@Test

Public void testMultipleSelectList()

{

    ...

    // Zaznaczenie wielu opcji na liście na podstawie widocznego tekstu

    color.selectByVisibleText("Czarny");

    color.selectByVisibleText("Czerwony");

    color.selectByVisibleText("Srebrny");

    // Sprawdzenie, czy na liście zaznaczono wiele opcji zgodnie z zawartością

    // tablicy

    List<String> expectedSelection = Arrays.asList

        ("Czarny", "Czerwony", "Srebrny");

    List<String> actualSelection = new ArrayList<String>();

    for(WebElement option : color.getAllSelectedOptions())

    {actualSelection.add(option.getText()); }

    // Sprawdzenie, czy elementy tablicy oczekiwanych wartości pasują do faktycznie

    // wybranych opcji

    assertArrayEquals (expectedSelection.toArray(),actualSelection.toArray());

    // Sprawdzenie, czy na liście zaznaczono 3 opcje

    assertEquals(3, color.getAllSelectedOptions().size());

    // Anulowanie zaznaczenia opcji na podstawie widocznego tekstu

    color.deselectByVisibleText("Srebrny");

    // Sprawdzenie liczby zaznaczonych opcji

    assertEquals(2, color.getAllSelectedOptions().size());

    // Anulowanie zaznaczenia opcji na podstawie atrybutu value

    color.deselectByValue("rd");

    // Sprawdzenie liczby zaznaczonych opcji

    assertEquals(1, color.getAllSelectedOptions().size());

    // Anulowanie zaznaczenia opcji przy użyciu indeksu opcji

    color.deselectByIndex(0);

    // Sprawdzenie liczby zaznaczonych opcji

    assertEquals(0, color.getAllSelectedOptions().size());

}

Jak to działa?

Gdy użytkownik wybierze opcję z rozwijanego menu lub listy, które obsługują wybór tylko jednej opcji, zaznaczoną opcję można odczytać za pomocą metody getFirstSelectedOption() klasy Select. Metoda ta zwraca opcję w postaci egzemplarza klasy WebElement. Przykładowo w menu rozwijanym Make za pomocą metody selectByVisible() wybraliśmy opcję Honda. Aby sprawdzić ten wybór, możemy użyć metod getFirstSelectedOption() i getText() w następujący sposób.

// Klasa Select pozwala zaznaczyć opcję menu rozwijanego na podstawie widocznego tekstu

    make.selectByVisibleText("Honda");

    assertEquals("Honda", make.getFirstSelectedOption().getText());

Sprawdzanie wybranych opcji w rozwijanych menu i listach obsługujących wybór wielokrotny

Aby sprawdzić wybrane opcje w rozwijanych menu i listach obsługujących wybór wielokrotny, możemy skorzystać z metody getAllSelectedOptions() klasy Select. Metoda zwraca wszystkie wybrane opcje jako listę egzemplarzy klasy WebElement. W poniższym teście utworzyliśmy listę oczekiwanych wybranych elementów, a następnie pobraliśmy listę wybranych opcji poprzez iterowanie po egzemplarzach klasy WebElement zwróconych przez metodę getAllSelectedOptions().

// Sprawdzenie, czy na liście zaznaczono wiele opcji zgodnie z zawartością tablicy

List<String> expectedSelection = Arrays.asList("Czarny",

    "Czerwony", "Srebrny");

List<String> actualSelection = new ArrayList<String>();

for(WebElement option : color.getAllSelectedOptions()) {

    actualSelection.add(option.getText()); }

Za pomocą metody assertArrayEquals() frameworka JUnit porównamy listy expectedSelection i actualSelection, aby sprawdzić, czy na liście zaznaczono odpowiednie opcje, tak jak pokazano w poniższym kodzie.

// Sprawdzenie, czy elementy tablicy oczekiwanych wartości pasują do faktycznie

// wybranych opcji

assertArrayEquals(expectedSelection.toArray(),

    actualSelection.toArray());

Możemy również sprawdzić liczbę opcji wybranych na liście poprzez zbadanie rozmiaru listy zwróconej przez metodę getAllSelectedOptions(). Przykładowo w poniższym kodzie na liście zostały wybrane trzy opcje, dlatego metoda getAllSelectedOptions().size() powinna zwrócić 3.

assertEquals(3, color.getAllSelectedOptions().size());

Co dalej?

Aby sprawdzić, czy zaznaczono określoną opcję, wystarczy zbadać tablicę actualSelection w następujący sposób.

assertTrue(actualSelection.contains("Czerwony"));

Zobacz też

Receptura „Zaznaczanie opcji w elementach Select”.

Automatyzacja przełączników i grup opcji

Framework Selenium WebDriver za pośrednictwem interfejsu WebElement obsługuje przełączniki i grupy opcji. Możemy zaznaczać i anulować zaznaczenie przełączników za pomocą metody click() klasy WebElement, a przy użyciu metody isSelected()sprawdzać, czy przełącznik jest zaznaczony, czy niezaznaczony.

W tej recepturze pokażę, w jaki sposób można pracować z przełącznikami i grupami opcji.

Jak to zrobić?

Utwórzmy test, który operuje na kontrolkach przełączników i grup opcji. Wykonamy operacje zaznaczania i anulowania zaznaczenia. Przykład wykonania tych operacji zaprezentowano w poniższym kodzie.

@Test

Public void testRadioButton() {}

    // Utworzenie reprezentacji przełącznika w postaci obiektu WebElement na podstawie jego atrybutu value

        WebElement petrol = driver.findElement(By

            .xpath("//input[@value='Benzyna']"));

    // Sprawdzenie, czy przełącznik jest już zaznaczony. Jeśli nie jest, zaznaczamy go

    // przez wywołanie metody click()

    if (!petrol.isSelected()) {

        petrol.click();

        }

        // Zweryfikowanie, czy przełącznik jest zaznaczony

        assertTrue(petrol.isSelected());

        // Można również uzyskać listę wszystkich przełączników z grupy opcji.

        // W tym celu należy przekazać identyfikator grupy opcji do metody findElements()

        List<WebElement> fuelType =

    driver.findElements(By.name("fuel_type"));

      for (WebElement type : fuelType) {

        // Wyszukaj przełącznik Diesel w grupie opcji i zaznacz go

        if (type.getAttribute("value").equals("Diesel")) {

            if(!type.isSelected()) {

                type.click();

            }

            assertTrue(type.isSelected());

            break;

        }

    }

}

Jak to działa?

Przełączniki można lokalizować podobnie jak dowolne inne elementy. W tym przykładzie do zlokalizowania przełącznika na podstawie jego atrybutu value skorzystano z wyrażenia XPath.

// Utworzenie obiektu WebElement reprezentującego przełącznik na podstawie atrybutu value

WebElement petrol = driver.findElement(By.xpath

    ("//input[@value='Benzyna']"));

Przełączniki można zaznaczać lub anulować ich zaznaczenie za pomocą metody click(). Nie istnieją odrębne metody do wykonywania tych operacji. Gdy chcemy zaznaczyć przełącznik, musimy być ostrożni i najpierw sprawdzić, czy nie został wcześniej zaznaczony. Jeśli tego nie zrobimy, wywołanie metody click() spowoduje usunięcie zaznaczenia przełącznika zamiast jego zaznaczenia. Aby sprawdzić, czy przełącznik jest zaznaczony, można wywołać metodę isSelected(), która zwraca true, jeśli przełącznik jest zaznaczony, i false, jeśli nie jest zaznaczony. W poniższym przykładzie metoda click() zostanie wywołana tylko wtedy, gdy przełącznik nie jest zaznaczony.

// Sprawdzenie, czy przełącznik jest już zaznaczony. Jeśli nie jest, zaznaczamy go

// przez wywołanie metody click()

if (!petrol.isSelected()) {

    petrol.click();

}

Grupy opcji

Zamiast lokalizowania pojedynczych przełączników można również pracować z grupami opcji. Za pomocą metody findElements() można zlokalizować wszystkie przełączniki z grupy reprezentowanej przez listę obiektów WebElement. W poniższym przykładzie pobrano listę obiektów WebElement reprezentujących przełączniki z grupy fuel_type.

// Możemy również uzyskać wszystkie przełączniki z grupy opcji

// w formie listy. W tym celu należy przekazać do metody findElements() identyfikator grupy opcji

List<WebElement> fuelType =

    driver.findElements(By.name("fuel_type"));

Następnie możemy iterować po tej liście w celu wyszukania określonego przełącznika albo też zaznaczyć określony przełącznik (lub anulować zaznaczenie), tak jak pokazano w poniższym przykładzie.

for (WebElement type : fuelType)

{

    // Wyszukanie w grupie opcji przełącznika Diesel i zaznaczenie go

    if(type.getAttribute("value").equals("Diesel"))

    {

        if(!type.isSelected()) {

          type.click();

}

        assertTrue(type.isSelected());

        break;

    }

}

Automatyzowanie pól wyboru

Framework Selenium WebDriver za pośrednictwem interfejsu WebElement obsługuje element checkbox. Możemy zaznaczać lub anulować zaznaczenie pól wyboru przy użyciu metody click(), a także sprawdzać, czy pole wyboru jest zaznaczone lub nie za pomocą metody isSelected().

W tej recepturze pokażę, jak pracować z polami wyboru.

Jak to zrobić?

Oto kod testu, który wykorzystuje pole wyboru do wykonywania operacji zaznaczania i usuwania zaznaczenia.

@Test

Public void testCheckBox() {}

    // Pobranie obiektu WebElement reprezentującego pole wyboru na podstawie jego atrybutu value

    WebElement airbags = driver.findElement(By.xpath("//input[@

value='Poduszki powietrzne']"));

    // Sprawdzenie, czy pole wyboru jest już zaznaczone. Jeśli nie jest zaznaczone, zaznaczamy pole wyboru

    // przez wywołanie metody click()

    if (!airbags.isSelected()) {

        airbags.click();

    }

    // Zweryfikowanie, czy pole wyboru jest zaznaczone

    assertTrue(airbags.isSelected());

    // Sprawdzenie, czy pole wyboru jest zaznaczone. Jeśli tak, to usuwamy jego zaznaczenie

    // przez wywołanie metody click()

    if (airbags.isSelected()) {

        airbags.click();

    }

    // Zweryfikowanie, czy pole wyboru jest niezaznaczone

    assertFalse(airbags.isSelected());

}

Jak to działa?

Pole wyboru możemy zlokalizować w sposób podobny do lokalizowania innych elementów na stronie. W tym przykładzie do zlokalizowania pola wyboru na podstawie jego atrybutu value wykorzystano XPath.

WebElement airbags = driver.findElement

    (By.xpath("//input[@value='Poduszki powietrzne']"));

Pola wyboru możemy zaznaczać lub anulować ich zaznaczenie przy użyciu metody click(). Nie istnieją odrębne metody do wykonywania tych operacji. Gdy chcemy zaznaczyć pole wyboru, musimy uważać, czy nie zostało ono zaznaczone wcześniej. W przeciwnym razie wywołanie metody click() spowoduje anulowanie zaznaczenia pola wyboru. Do sprawdzenia, czy pole wyboru jest zaznaczone, można wywołać metodę isSelected(). Metoda ta zwraca true, jeśli pole jest zaznaczone, i false w przeciwnym przypadku. W poniższym fragmencie kodu metoda click() zostanie wywołana tylko wtedy, gdy pole wyboru nie jest zaznaczone.

if (!airbags.isSelected()) {

    airbags.click();

}

I podobnie, aby usunąć zaznaczenie pola wyboru, trzeba sprawdzić, czy nie jest ono już zaznaczone.

if (airbags.isSelected()) {

    airbags.click();

}

Praca z obiektami WebTable

Element HTML table, podobnie jak inne typy elementów, z którymi zetknęliśmy się we wcześniejszych recepturach, jest reprezentowany za pomocą obiektu WebElement. Interfejs WebElement nie udostępnia jednak żadnych metod specyficznych dla tabel.

Podczas pracy z tabelami wiersze i komórki można skutecznie wyszukiwać za pomocą zestawu metod klasy By. W tej recepturze pokażę, w jaki sposób pobrać z tabeli wiersze i kolumny.

Jak to zrobić?

Utwórzmy prosty test, który wyświetla dane z tabeli, tak jak pokazano w poniższym przykładowym kodzie.

@Test

public void testWebTable() {

    WebElement simpleTable = driver.findElement(By.id("items"));

    // Pobranie wszystkich wierszy

    List<WebElement> rows =

simpleTable.findElements(By.tagName("tr"));

    assertEquals(3,rows.size());

    // Wyświetlenie danych z każdego wiersza

    for (WebElement row : rows) {

        List<WebElement> cols = row.findElements(By.tagName("td"));

        for (WebElement col : cols) {

            System.out.print(col.getText() + "\t");

        }

        System.out.println();

    }

}

Jak to działa?

Tabela w HTML jest zbiorem elementów <tr> i <td> reprezentujących — odpowiednio — wiersze i komórki. W przykładowym teście reprezentację tabeli w postaci obiektu WebElement można uzyskać na podstawie jej atrybutu ID w następujący sposób.

WebElement simpleTable = driver.findElement(By.id("items"));

W celu pobrania wszystkich wierszy z tabeli wywołano metodę findElements() obiektu simpleTable. Aby uzyskać wszystkie elementy <tr>, wykorzystano strategię tagName, tak jak pokazano poniżej.

List<WebElement> rows =

    simpleTable.findElements(By.tagName("tr"));

Każdy element <tr> zawiera elementy <td>, które reprezentują kolumny (komórki) tabeli. W celu wyświetlenia danych test iteruje po wierszach i kolumnach w następujący sposób.

// Wyświetlenie danych z każdego wiersza

for (WebElement row : rows) {

    List<WebElement> cols = row.findElements(By.tagName("td"));

    for (WebElement col : cols) {

        System.out.print(col.getText() + "\t");

    }

    System.out.println();

}

Metoda przydaje się, gdy w teście trzeba zweryfikować dane w tabeli.

Co dalej?

Do wyszukiwania wierszy i komórek z wykorzystaniem techniki dopasowywania indeksów możemy również użyć selektorów CSS lub XPath. W poniższym przykładzie zastosowano selektor CSS do znalezienia pierwszej komórki drugiego wiersza w tabeli.

WebElement cell = driver.findElement

    (By.cssSelector("table#items tbody tr:nth-child(2) td"));

W podobny sposób można skorzystać z wyrażenia XPath.

WebElement cell = driver.findElement    (By.xpath("//table[@id='items']/tbody/tr[2]/td"));

Rozdział 4.
Korzystanie z Selenium API

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Sprawdzanie istnienia elementu.

Sprawdzanie stanu elementu.

Wykorzystanie API zaawansowanych interakcji użytkownika (ang. Advanced User Interactions) do obsługi zdarzeń myszy i klawiatury.

Wykonywanie podwójnych kliknięć elementu.

Wykonywanie operacji „przeciągnij i upuść”.

Obsługa menu kontekstowych.

Wykonywanie kodu JavaScript.

Przechwytywanie zrzutów ekranu za pomocą Selenium WebDriver.

Maksymalizowanie okna przeglądarki.

Obsługa plików cookie sesji.

Obsługa mechanizmów nawigacji przeglądarki.

Korzystanie ze zdarzeń klasy WebDriver.

Wprowadzenie

Selenium WebDriver implementuje kompleksowe API do pracy z elementami webowymi, które umożliwia wykonywanie zaawansowanych interakcji z użytkownikiem, takich jak złożone zdarzenia związane z myszą i klawiaturą, wykonywanie kodu JavaScript, przechwytywanie zrzutów ekranu i tak dalej.

W tym rozdziale omówię sposób wykorzystania tych własności do budowania kroków testów o różnym poziomie złożoności. Lektura rozdziału pomoże również w przezwyciężeniu niektórych typowych problemów, które mogą powstawać podczas tworzenia testów bazujących na frameworku Selenium WebDriver.

Sprawdzanie istnienia elementu

Selenium WebDriver nie implementuje metody Selenium RC isElementPresent(), która sprawdza, czy element istnieje na stronie. Metoda jest przydatna do budowania niezawodnego testu, który mógłby sprawdzić istnienie elementu przed wykonaniem na nim jakiegokolwiek działania.

W tej recepturze napiszę metodę podobną do metody isElementPresent().

Jak to zrobić?

Aby zaimplementować metodę isElementPresent(), wykonaj następujące czynności.

Utwórz metodę isElementPresent() w module dostępnym do testów i umieść w niej następujący kod.

{{Private boolean isElementPresent(By by)

    try {

        driver.findElement(by);

            return true;

        } catch {} (NoSuchElementException e)

            return false;

    }

}

Teraz utwórz kod testu, który wywołuje metodę isElementPresent(). Metoda będzie sprawdzać, czy żądany element istnieje na stronie. Jeśli będzie znaleziony, zostanie wykonane kliknięcie elementu, w przeciwnym razie test nie przejdzie. Można to zrobić w następujący sposób.

@Test

Public void testIsElementPresent() {}

    // Sprawdzenie, czy element spełniający kryteria lokalizatora istnieje na stronie

    if (isElementPresent(By.name("airbags"))) {

        // Utwórz reprezentację pola wyboru i zaznacz je

        WebElement airbag = driver.findElement(By.name("airbags"));

        if (!airbag.isSelected()) {

            airbag.click();

        }

    } else {

        fail("Pole wyboru poduszki powietrznej nie istnieje!!");

    }

}

Jak to działa?

Metoda isElementPresent() pobiera argument lokalizatora z wykorzystaniem egzemplarza klasy By. Następnie wywołuje metodę findElement(). Jeśli element nie zostanie znaleziony, będzie zgłoszony wyjątek NoSuchElementException. Dzięki użyciu bloku try i catch metoda isElementPresent() zwróci true, gdy element zostanie znaleziony. W tym przypadku nie zostanie zgłoszony wyjątek. W przeciwnym razie zwróci false, a metoda findElement() zgłosi wyjątek NoSuchElementException.

Zobacz też

Receptura „Sprawdzanie stanu elementu”.

Sprawdzanie stanu elementu

Wielokrotnie zdarza się, że próba kliknięcia elementu lub wprowadzenia tekstu w polu zawodzi ze względu na to, że element jest wyłączony lub istnieje w modelu DOM, ale jest ukryty na stronie. To powoduje zgłoszenie błędu i niepowodzenie testu. Zbudowanie wiarygodnych testów, które można uruchomić w trybie nienadzorowanym, wymaga utworzenia w przepływie testów solidnego mechanizmu obsługi błędów i wyjątków.

Powstające problemy można obsłużyć, sprawdzając stan elementów. Interfejs WebElement udostępnia następujące metody sprawdzania stanu elementu.

Metoda

Przeznaczenie

isEnabled()

Metoda sprawdza, czy element jest aktywny. Zwraca true, jeśli jest aktywny, i false w przypadku przeciwnym.

isSelected()

Metoda sprawdza, czy element (przełącznik, pole wyboru i tak dalej) jest zaznaczony. Zwraca true, jeśli jest zaznaczony, i false w przypadku przeciwnym.

isDisplayed()

Metoda sprawdza, czy element jest wyświetlany.

W tej recepturze zastosuję niektóre z wymienionych metod w celu sprawdzenia stanu elementów i obsługi błędów, jeśli to możliwe.

Jak to zrobić?

Utworzymy test, w ramach którego sprawdzimy, czy na stronie zostało zaznaczone pole wyboru włączające reflektor LED. To pole wyboru będzie włączone lub wyłączone, w zależności od wcześniej wybranych opcji. Przed zaznaczeniem tego pola wyboru upewnimy się, czy jest ono aktywne. Można to zrobić w następujący sposób.

@Test

Public void testElementIsEnabled() {}

    // Utworzenie reprezentacji pola wyboru w postaci obiektu WebElement z wykorzystaniem atrybutu name

    WebElement ledheadlamp = driver.findElement(By.name("ledheadlamp"));

    // Sprawdzenie, czy element jest aktywny przed jego zaznaczeniem

    if (ledheadlamp.isEnabled()) {

        // Sprawdzenie, czy pole wyboru już jest zaznaczone. W przeciwnym razie zaznaczenie go

         if (!ledheadlamp.isSelected()) {

            ledheadlamp.click();

        }

    } else {

         fail("Pole wyboru reflektora LED jest nieaktywne!!");

    }

}

Jak to działa?

Zaznaczamy pole wyboru, sprawdzając dwa stany elementu; po pierwsze, czy pole to jest aktywne, i po drugie, czy już nie jest zaznaczone. Do tego celu możemy użyć funkcji isEnabled() interfejsu WebElement, która zwraca true, jeśli element jest aktywny, lub false, gdy jest nieaktywny. Jeśli pole wyboru jest nieaktywne, test zakończy się niepowodzeniem. Kiedy nie sprawdzimy tego warunku, test prawdopodobnie zgłosi wyjątek informujący o tym, że obiekt jest nieaktywny, tak jak pokazano poniżej.

// Sprawdzenie, czy element jest aktywny przed jego zaznaczeniem

if (ledheadlamp.isEnabled()) {

    // Sprawdzenie, czy pole wyboru jest już zaznaczone. W przeciwnym razie zaznaczenie pola wyboru

    if (!ledheadlamp.isSelected()) {

        ledheadlamp.click();

    }

} else {

    fail("Pole wyboru reflektora LED jest nieaktywne!!");

}

Wykorzystanie API Advanced User Interactions do obsługi zdarzeń myszy i klawiatury

API Advanced User Interactions frameworka Selenium WebDriver pozwala wykonywać zarówno proste, jak i złożone operacje obsługi zdarzeń klawiatury i myszy. Klasa Actions udostępnia tak złożone operacje jak „przeciągnij i upuść”, czy przytrzymanie klawisza, a następnie wykonanie operacji myszą. API umożliwia także budowanie złożonych łańcuchów zdarzeń imitujących działania ręcznie wykonywane przez użytkownika.

W celu utworzenia złożonych działań obejmujących grupę innych działań klasa Actions implementuje wzorzec „budowniczy”.

W tej recepturze użyję klasy Actions do utworzenia łańcucha zdarzeń wymaganych do zaznaczenia wierszy w tabeli.

Jak to zrobić?

Utwórzmy test, który zaznacza wiele wierszy z różnych pozycji w tabeli za pomocą klawisza Ctrl (w komputerach Mac Command). Możemy wybrać wiele wierszy: zaznaczamy pierwszy wiersz, przytrzymujemy klawisz Ctrl (Command na komputerach Mac), a następnie wybieramy inny wiersz i zwalniamy klawisz Ctrl (Command). Takie działanie spowoduje zaznaczenie żądanych wierszy z tabeli, tak jak pokazano w poniższym fragmencie kodu.

@Test

public void testRowSelectionUsingControlKey() {

    List<WebElement> tableRows = driver.findElements

        (By.xpath("//table[@class='iceDatTbl']/tbody/tr"));

    // Zaznacz drugi i czwarty wiersz w tabeli za pomocą klawisza Control.

    // Wiersze rozpoczynają się od indeksu 0

    Actions builder = new Actions(driver);

    builder.click(tableRows.get(1))

           .keyDown(Keys.CONTROL)

           .click(tableRows.get(3))

           .keyUp(Keys.CONTROL)

           .build().perform();

// Sprawdzenie, czy w tabeli są zaznaczone dwa wiersze

List<WebElement> rows = driver.findElements

    (By.xpath("//div[@class='icePnlGrp

    exampleBox']/table[@class='iceDatTbl']/tbody/tr"));

assertEquals(2,rows.size());

}

W komputerach z systemem Mac OS X należy użyć składni klawisza Command zamiast składni klawisza Control. Oto przykład.

Actions builder = new Actions(driver);

builder.click(tableRows.get(1)).keyDown(Keys.COMMAND)

    click(tableRows.get(3)).keyUp(Keys.COMMAND).perform();}

Jak to działa?

Należy utworzyć egzemplarz klasy Actions, przekazując do konstruktora egzemplarz klasy driver w następujący sposób.

Actions builder = new Actions(driver);

Zbudujemy łańcuch zdarzeń, które należy wykonać, aby zaznaczyć wiersze. Będzie to wymagało wykonania operacji click() na pierwszym wierszu, następnie przytrzymania klawisza Ctrl (Command w systemie Mac) przy użyciu operacji keyDown(), kliknięcia wiersza końcowego, a następnie zwolnienia klawisza Ctrl (Command) przez wywołanie keyUp(). Klasa Actions oferuje różne metody do wykonywania operacji z klawiaturą i myszą.

Actions builder = new Actions(driver);

builder.click(tableRows.get(1)).keyDown(Keys.CONTROL)

    .click(tableRows.get(3)).keyUp(Keys.CONTROL)

    .build().perform();

Możemy utworzyć złożone działania, które wykonamy przy użyciu wywołania metody perform() klasy Actions.

Klasa Keys reprezentuje wszystkie nietekstowe klawisze na klawiaturze, na przykład klawisz Ctrl, klawisz Shift, klawisze funkcyjne i tak dalej. W poprzednim przykładzie użyliśmy wywołania keyDown(Keys.CONTROL) w celu naciśnięcia i przytrzymania klawisza Ctrl (Command) aż do zakończenia następnej operacji.

Działania mogą nie uruchamiać się poprawnie w przypadku elementów, które nie są widoczne lub są nieaktywne. Przed użyciem opisanych zdarzeń należy zadbać o to, aby elementy były widoczne i aktywne.

Zobacz też

Receptura „Wykonywanie dwukrotnych kliknięć elementów”.

Wykonywanie dwukrotnych kliknięć elementów

W aplikacjach webowych są elementy, które do wykonania niektórych czynności wymagają dwukrotnych kliknięć. Przykładowo dwukrotne kliknięcie wiersza tabeli powoduje wyświetlenie nowego okna. API Advanced User Interaction dostarcza metodę umożliwiającą wykonywanie dwukrotnych kliknięć.

W tej recepturze do wykonywania operacji dwukrotnego kliknięcia użyję klasy Actions.

Jak to zrobić?

Utwórzmy test lokalizujący element, dla którego zaimplementowano zdarzenie dwukrotnego kliknięcia. Dwukrotne kliknięcie tego elementu spowoduje zmianę jego koloru.

package com.secookbook.examples.chapter04;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.interactions.Actions;

import static org.junit.Assert.*;

import org.junit.Test;

public class DoubleClickTest {

    @Test

    public void testDoubleClick() throws Exception {

        WebDriver driver = new ChromeDriver();

        driver.get("http://cookbook.seleniumacademy.com/DoubleClickDemo.html");

        try {

            WebElement message = driver.findElement(By.id("message"));

            // Sprawdzenie, czy element jest niebieskiego koloru

            assertEquals("rgba(0, 0, 255, 1)", message.getCssValue("background-color"));

            Actions builder = new Actions(driver);

            builder.doubleClick(message).perform();

            // Sprawdzenie, czy element jest żółtego koloru

            assertEquals("rgba(255, 255, 0, 1)", message.getCssValue("background-color"));

        } finally {

            driver.quit();

        }

    }

}

Jak to działa?

Aby wykonać dwukrotne kliknięcie elementu, należy wywołać metodę doubleClick() klasy Actions. W celu wywołania tej metody trzeba utworzyć egzemplarz klasy Actions, tak jak pokazano poniżej.

Actions builder = new Actions(driver);

Metoda doubleClick() wymaga elementu, na którym zostanie zainicjowane zdarzenie dwukrotnego kliknięcia. Metodę doubleClick() można wywołać, przekazując element w następujący sposób.

builder.doubleClick(message).perform();

Zobacz też

Receptura „Wykorzystanie API Advanced User Interactions do obsługi zdarzeń myszy i klawiatury”,

Receptura „Wykonywanie operacji «przeciągnij i upuść»”.

Wykonywanie operacji „przeciągnij i upuść”

Selenium WebDriver, za pośrednictwem klasy Actions, implementuje polecenie Selenium RC dragAndDrop. Klasa Actions, jak pokazywałem we wcześniejszych recepturach, obsługuje zaawansowane interakcje użytkownika, takie jak wyzwalanie zdarzeń myszy i klawiatury. Za pośrednictwem tej klasy można budować proste lub złożone łańcuchy zdarzeń.

W tej recepturze wykorzystam klasę Actions do wykonywania operacji „przeciągnij i upuść”.

Jak to zrobić?

Zaimplementujemy test, który za pomocą klasy Actions wykona na stronie operację „przeciągnij i upuść”.

@Test

Public void testDragDrop() {}

    Driver.get ("http://cookbook.seleniumacademy.com/DragDropDemo.html");

    WebElement source = driver.findElement(By.id("draggable"));

    WebElement target = driver.findElement(By.id("droppable"));

    Actions builder = new Actions(driver);

    builder.dragAndDrop(source, target) .perform();

    assertEquals("Upuszczono!", target.getText());

}

Jak to działa?

Aby przeciągnąć element na inny element i go na nim upuścić, trzeba zlokalizować oba elementy i przekazać je do metody dragAndDrop() klasy Actions. Żeby wywołać tę metodę, należy utworzyć egzemplarz klasy Actions w następujący sposób.

Actions builder = new Actions(driver);

Metoda dragAndDrop() wymaga elementu źródłowego oraz elementu docelowego, na który element źródłowy zostanie przeciągnięty i upuszczony. Metodę dragAndDrop() można wywołać w następujący sposób.

builder.dragAndDrop(source, target).perform();

W czasie powstawania tej książki framework Selenium WebDriver nie obsługiwał operacji „przeciągnij i upuść” dla HTML5. Więcej informacji na ten temat można znaleźć pod adresem https://code.google.com/p/selenium/issues/detail?id=3604. Istnieje obejście umożliwiające symulację własności „przeciągnij i upuść” w aplikacjach webowych bazujących na HTML5. Więcej szczegółów na ten temat można znaleźć pod adresem https://gist.github.com/rcorreia/2362544.

Zobacz też

Receptura „Wykorzystanie API Advanced User Interactions do obsługi zdarzeń myszy i klawiatury”,

Receptura „Wykonywanie dwukrotnych kliknięć elementów”.

Obsługa menu kontekstowych

Menu kontekstowe (znane także jako menu skrótów, wyskakujące menu lub menu podręczne) to lista poleceń wyświetlanych na stronie WWW w odpowiedzi na kliknięcie elementu prawym przyciskiem myszy. Przykładowo pod adresem http://bit.ly/1CAV05I zamieszczono kod korzystający z wtyczki jQuery contextMenu, który wyświetla menu edycji, gdy użytkownik kliknie przycisk prawym przyciskiem myszy.

Klasa Actions frameworka Selenium WebDriver dostarcza metodę contextClick(), która pozwala wykonać operację kliknięcia prawym przyciskiem myszy. W tej recepturze pokażę, w jaki sposób zautomatyzować interakcje z menu kontekstowymi.

Jak to zrobić?

Spróbujmy zaimplementować test, który otworzy menu kontekstowe i, korzystając z klasy Actions, wybierze jedną z opcji.

@Test

Public void testContextMenu() {}

    WebElement clickMeElement =

        driver.findElement (By.cssSelector ("div.context-menuone.Box.menu-1 "));

    WebElement editMenuItem =

        driver.findElement (By.cssSelector ("li.context-menuitem.icon-edit"));

    Actions builder = new Actions(driver);

    builder.contextClick(clickMeElement)

        .moveToElement(editMenuItem)

        .Click()

        .perform();

    WebDriverWait wait = new WebDriverWait(driver, 10);

    Alert alert = wait.until(ExpectedConditions.alertIsPresent());

    assertEquals("clicked: edit", alert.getText());

    alert.dismiss();

}

Jak to działa?

Do otwarcia menu kontekstowego w odpowiedzi na kliknięcie prawym przyciskiem myszy służy metoda contextClick() klasy Actions. Poniższy kod wykonuje kliknięcie prawym przyciskiem myszy podanego elementu.

WebElement clickMeElement =

        driver.findElement(By.cssSelector("div.context-menuone.

box.menu-1"));

    WebElement editMenuItem =

        driver.findElement(By.cssSelector("li.context-menuitem.

icon-edit"));

    Actions builder = new Actions(driver);

    builder.contextClick(clickMeElement)

        .moveToElement(editMenuItem)

        .click()

        .perform();

Następnie można przejść do żądanego elementu — w tym przypadku jest nim element <li>, który reprezentuje jedną z opcji menu. Aby wykonać akcję menu, wywoływana jest metoda click(). W tym przykładzie wywołanie metody click() powoduje wyświetlenie alertu, a test sprawdza komunikat w oknie alertu.

Co dalej?

W poprzednim przykładzie test wyszukuje pozycję menu, a następnie wykonuje operację kliknięcia. Czasami w menu są dostępne klawisze skrótów. Za pośrednictwem klasy Actions można wykorzystać kombinację zdarzeń myszy i naciśnięć klawiszy w celu otwarcia żądanej opcji menu. Przykładowo klawiszem skrótu dla pozycji menu Edit jest „e”. Możemy otworzyć menu kontekstowe, a następnie wysłać kombinację klawiszy Alt+E w następujący sposób.

@Test

public void testContextMenuWithKeys() {

    WebElement clickMeElement =

        driver.findElement(By.cssSelector("div.context-menu-one.box.menu-1"));

    Actions builder = new Actions(driver);

    builder.contextClick(clickMeElement)

    .sendKeys(Keys.chord(Keys.ALT, "e"))

    .perform();

    WebDriverWait wait = new WebDriverWait(driver, 10);

    Alert alert = wait.until(ExpectedConditions.alertIsPresent());

    assertEquals("clicked: edit", alert.getText());

    alert.dismiss();

}

Zobacz też

Receptura „Wykorzystanie API Advanced User Interactions do obsługi zdarzeń myszy i klawiatury”.

Wykonywanie kodu JavaScript

API Selenium WebDriver zapewnia możliwość wykonywania w oknie przeglądarki kodu JavaScript. Jest to bardzo przydatna funkcja, w przypadku gdy testy muszą wykonywać operacje na stronie przy użyciu JavaScript. Dzięki temu API frameworka Selenium WebDriver może być wykorzystane również do testowania kodu JavaScript po stronie klienta. Selenium WebDriver oferuje interfejs JavascriptExecutor, który może służyć do uruchamiania dowolnego kodu JavaScript w kontekście przeglądarki.

W tej recepturze pokażę, w jaki sposób używać interfejsu JavascriptExecutor do uruchamiania kodu JavaScript. W tej książce zamieszczono różne receptury, w których wykorzystano interfejs JavascriptExecutor do wykonywania niektórych zaawansowanych operacji, które nie są jeszcze obsługiwane przez Selenium WebDriver.

Jak to zrobić?

Utwórzmy test, który wywołuje kod JavaScript w celu zwrócenia tytułu i liczby linków (tzn. liczby tagów Anchor). Aby zwrócić tytuł strony, można również wykorzystać metodę driver.getTitle(). Poniżej przedstawiono przykładowy kod takiego działania.

@Test

public void testJavaScriptCalls() throws Exception {

    WebDriver driver = new ChromeDriver();

    driver.get("http://www.google.com");

    try {

        JavascriptExecutor js = (JavascriptExecutor) driver;

        String title = (String) js.executeScript("return document.title");

        assertEquals("Google", title);

        long links = (Long) js

            .executeScript("var links = document.getElementsByTagName('A'); return links.length");

      assertEquals(42, links);

    } finally {

      driver.quit();

    }

}

Jak to działa?

Zrzutowanie egzemplarza WebDriver na interfejs JavascriptExecutor pozwala wykonać w Selenium WebDriver kod JavaScript.

JavascriptExecutor js = (JavascriptExecutor) driver;

W poniższym przykładzie uruchomiono pojedynczy wiersz kodu JavaScript, który zwraca tytuł strony wyświetlanej przez sterownik. Interfejs JavascriptExecutor dostarcza metodę executeScript(), do której należy przekazać kod JavaScript.

String title = (String) js.executeScript("return document.title");

Podczas zwracania wartości z kodu JavaScript trzeba użyć słowa kluczowego return. Wynik metody executeScript()trzeba zrzutować na podstawie typu zwróconej wartości. Dla wartości dziesiętnych można użyć typu Double, niedziesiętne wartości liczbowe wymagają użycia typu Long, natomiast dla wartości logicznych należy stosować typ Boolean. Jeśli kod JavaScript zwraca element HTML, można wykorzystać typ WebElement. Dla wartości tekstowych należy skorzystać z typu String. Jeśli jest zwracana lista obiektów, można zastosować dowolny z wymienionych typów, w zależności od typu obiektów na liście. W przeciwnym razie zostanie zwrócona wartość null.

W poniższym przykładzie w celu pobrania liczby linków na stronie uruchomiono wielowierszowy kod JavaScript.

long links = (Long) js.executeScript("var links =

    document.getElementsByTagName('A'); return links.length");

Co dalej?

Do kodu JavaScript uruchamianego za pomocą metody executeScript() mogą być przekazywane argumenty. W poniższym przykładzie chcemy ustawić wartość elementu. Wewnątrz kodu JavaScript zostanie wykorzystana specjalna tablica arguments, tak jak pokazano w poniższym kodzie.

js.executeScript("document.getElementByID('name').value =

    arguments[0]","Jan");

Przechwytywanie zrzutów ekranu za pomocą Selenium WebDriver

Selenium WebDriver dostarcza interfejs TakesScreenshot, który umożliwia przechwytywanie zrzutów ekranu stron internetowych. Pomaga to podczas uruchamiania testu, ponieważ pokazuje, co dokładnie się stało, gdy został zgłoszony wyjątek lub wystąpił błąd podczas uruchamiania. Można również przechwytywać zrzuty ekranu podczas weryfikacji stanu elementu, wartości wyświetlanych wewnątrz elementów lub stanu po zakończeniu działania.

Przechwytywanie zrzutów ekranu pomaga także w weryfikacji takich obszarów jak układ strony czy sposób wyrównania pól, kiedy porównywane są zrzuty ekranu wykonane podczas działania testów z ilustracjami bazowymi.

W tej recepturze użyję interfejsu TakesScreenshot do wykonania zrzutu ekranu testowanej strony WWW.

Jak to zrobić?

Utwórzmy test, który otworzy aplikację testową i wykona zrzut ekranu strony w formacie PNG (ang. Portable Network Graphics), tak jak pokazano w poniższym przykładowym kodzie.

@Test

public void testTakesScreenshot() throws Exception {

    File scrFile = ((TakesScreenshot) driver)

        .getScreenshotAs(OutputType.FILE);

    FileUtils.copyFile(scrFile, new File("target/main_page.png"));

}

Jak to działa?

Interfejs TakesScreenshot dostarcza metodę getScreenshotAs(), która pozwala przechwycić zrzut ekranu strony wyświetlanej wewnątrz egzemplarza obiektu driver. W poniższym przykładzie użyliśmy wartości OutputType.FILE jako argumentu metody getScreenshotAs(). Dzięki temu przechwycony zrzut ekranu zostanie zwrócony w pliku.

File scrFile =

    ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);

Obiekt file zwrócony przez metodę getScreenshotAs() możemy zapisać za pomocą metody copyFile() klasy FileUtils należącej do klasy org.apache.commons.io.FileUtils.

Interfejs TakesScreenshot wykonuje zrzuty ekranu z wykorzystaniem API przeglądarki. Sterownik HtmlUnit nie obsługuje interfejsu TakesScreenshot.

Co dalej?

Klasa OutputType oferuje wiele sposobów prezentowania wyników wykonania zrzutów ekranu za pomocą metody getScreenshotAs(). W poprzednim przykładzie widzieliśmy zrzut ekranu zapisany w pliku. Zrzuty ekranu mogą być również przechwytywane w tekstowym formacie Base64 lub w formacie surowych bajtów. W poniższym przykładzie wykonano zrzut ekranu w formacie ciągu Base64.

String base64 =

    ((TakesScreenshot)driver).getScreenshotAs(OutputType.BASE64);

Przechwytywanie zrzutów ekranu w środowisku RemoteWebDriver (Selenium Grid)

Gdy testy są uruchamiane z wykorzystaniem interfejsu RemoteWebDriver lub Selenium Grid, nie można wykonywać zrzutów ekranu, ponieważ interfejs TakesScreenshot nie jest zaimplementowany w klasie RemoteWebDriver.

Możemy jednak użyć klasy Augmenter, która dodaje interfejs TakesScreenshot do egzemplarza RemoteWebDriver, tak jak pokazano w poniższym przykładzie kodu.

driver = new Augmenter().augment(driver);

File scrFile =

    ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE);

FileUtils.copyFile(scrFile, new File("c:\\tmp\\screenshot.png"));

Klasa Augmenter ulepsza klasę RemoteWebDriver poprzez dodanie do niej różnych interfejsów, w tym interfejsu TakesScreenshot.

driver = new Augmenter().augment(driver);

Po wykonaniu takiego rozszerzenia możemy użyć interfejsu TakesScreenshot z klasy RemoteWebDriver do przechwytywania ekranu.

Zobacz też

Receptura „Przechwytywanie zrzutów ekranu elementów w Selenium WebDriver” w rozdziale 9., „Rozszerzanie Selenium”.

Receptura „Porównywanie obrazów w Selenium” w rozdziale 9., „Rozszerzanie Selenium”.

Maksymalizowanie okna przeglądarki

W Selenium WebDriver brakowało polecenia windowMaximize() z API Selenium RC. Jednakże od wersji 2.21 Selenium WebDriver obsługuje maksymalizowanie okna przeglądarki.

W tej krótkiej recepturze pokażę, jak zmaksymalizować okno przeglądarki.

Przygotuj się

Utwórz nowy test, który zbuduje egzemplarz WebDriver, przejdzie do witryny i wykona kilka podstawowych działań i weryfikacji.

Jak to zrobić?

Aby zmaksymalizować okno przeglądarki, trzeba wywołać metodę maximize() interfejsu Window klasy driver. Dodaj drugi wiersz kodu z poniższego fragmentu, ten za instrukcją definiującą egzemplarz klasy FirefoxDriver.

driver = new FirefoxDriver();

driver.manage().window().maximize();

Obsługa plików cookie sesji

W witrynach internetowych używane są pliki cookie do przechowywania preferencji użytkownika, informacji o logowaniu oraz różnych innych szczegółów dotyczących klienta. Selenium WebDriver API zapewnia różne metody zarządzania plikami cookie podczas testowania. Za pomocą tych metod możemy odczytywać wartości plików cookie, dodawać pliki cookie oraz usuwać takie pliki podczas testowania. Może to służyć do testowania reakcji aplikacji podczas wykonywania działań z plikami cookie. Interfejs WebDriver.Options udostępnia następujące metody zarządzania plikami cookie.

Metoda

Opis

addCookie(Cookie cookie)

Dodaje plik cookie.

getCookieNamed(String name)

Zwraca plik cookie o podanej nazwie.

getCookies()

Zwraca wszystkie pliki cookie dla bieżącej domeny.

deleteCookieNamed(String name)

Usuwa plik cookie o podanej nazwie.

deleteCookie(Cookie cookie)

Usuwa plik cookie.

deleteAllCookies()

Usuwa wszystkie pliki cookie dla bieżącej domeny.

W tej recepturze pokażę, jak odczytywać pliki cookie i sprawdzać ich wartość.

Przygotuj się

Utwórz nowy test, który zbuduje egzemplarz WebDriver, przejdzie do witryny i wykona kilka podstawowych działań i weryfikacji.

Jak to zrobić?

Utwórzmy test, który odczytuje plik cookie i sprawdza jego wartość, tak jak pokazano w poniższym przykładzie kodu.

@Test

Public void testCookies() {}

    Driver.get ("http://demo.magentocommerce.com/");

    // Utworzenie reprezentacji rozwijanego menu wyboru języka w postaci egzemplarza klasy Select

    Select language = new Select(driver.findElement(By

        .id("select-language")));

    // Sprawdzenie, czy domyślnie zaznaczona opcja to język polski

    assertEquals("Polski", language.getFirstSelectedOption().getText());

    // Zmienna storeCookie powinna mieć wartość null

    Cookie storeCookie = driver.manage().getCookieNamed("store");

    assertEquals(null, storeCookie);

    // Zaznacz opcję przy użyciu metody SelectByVisibleText

    language.selectByVisibleText("English");

    // Zmienna storeCookie powinna zawierać informację o wybranym języku

    storeCookie = driver.manage().getCookieNamed("store");

    assertEquals("english", storeCookie.getValue());

}

Jak to działa?

Interfejs WebDriver.Options dostarcza różne metody do dodawania, czytania, modyfikowania i usuwania plików cookie. W tym przykładzie, gdy zmieniamy język sklepu, do przechowywania preferencji językowych używany jest plik cookie. Ten plik cookie oraz jego wartość możemy odczytać w następujący sposób.

Cookie storeCookie = driver.manage().getCookieNamed("store");

assertEquals("english", storeCookie.getValue());

Wywołaliśmy metodę getCookieNamed(), przekazując nazwę pliku cookie. Metoda zwraca egzemplarz obiektu Cookie. Obiekt Cookie zawiera różne metody czytania wartości, domeny i tak dalej.

Korzystanie z mechanizmów nawigacji przeglądarki

Przeglądarki zapewniają wiele metod nawigacji umożliwiających dostęp do stron WWW z historii przeglądarki. Pozwalają również na odświeżanie bieżącej strony za pomocą przycisków Wstecz, Dalej oraz Odśwież na pasku narzędzi okna przeglądarki. API Selenium WebDriver zapewnia dostęp do tych przycisków za pomocą różnych metod interfejsu WebDriver.Navigation. Przy użyciu tych metod możemy testować zachowanie aplikacji.

Metoda

Opis

back()

Metoda powoduje przejście do poprzedniej strony w historii przeglądarki.

forward()

Metoda powoduje przejście do następnej strony w historii przeglądarki.

refresh()

Metoda powoduje ponowne załadowanie bieżącej strony.

to(String url)

to(java.net.URL url)

Metoda ładuje podany adres URL w bieżącym oknie przeglądarki.

W tej recepturze zaprezentuję metody nawigacji w przeglądarce.

Przygotuj się

Utwórz nowy test, który zbuduje egzemplarz klasy WebDriver, przejdzie do witryny i wykona kilka podstawowych działań i weryfikacji.

Jak to zrobić?

Utwórzmy test, który wywołuje różne metody nawigacji i sprawdza zachowanie aplikacji. Jego kod zamieszczono poniżej.

@Test

public void testNavigation() {

    driver.get("http://www.google.com");

    // Utworzenie reprezentacji pola tekstowego wyszukiwania

    WebElement searchField = driver.findElement(By.name("q"));

    searchField.clear();

    // Wprowadzenie słowa kluczowego do wyszukiwania i przesłanie żądania

    searchField.sendKeys("selenium webdriver");

    searchField.submit();

    WebElement resultLink = driver.findElement(By.linkText("Selenium WebDriver"));

    resultLink.click();

    new WebDriverWait(driver, 10).until(ExpectedConditions

        .titleIs("Selenium WebDriver"));

    assertEquals("Selenium WebDriver", driver.getTitle());

    driver.navigate().back();

    new WebDriverWait(driver, 10).until(ExpectedConditions

        .titleIs("selenium webdriver - Szukaj w Google"));

    assertEquals("selenium webdriver - Szukaj w Google", driver.getTitle());

    driver.navigate().forward();

    new WebDriverWait(driver, 10).until(ExpectedConditions

        .titleIs("Selenium WebDriver"));

    assertEquals("Selenium WebDriver", driver.getTitle());

    driver.navigate().refresh();

    new WebDriverWait(driver, 10).until(ExpectedConditions

        .titleIs("Selenium WebDriver"));

    assertEquals("Selenium WebDriver", driver.getTitle());

}

Jak to działa?

Interfejs WebDriver.Navigation dostarcza metody back() i forward() do ładowania stron z historii przeglądarki. Metody te reprezentują przyciski Wstecz i Dalej dostępne w dowolnej przeglądarce WWW. Możemy również odświeżyć lub ponownie załadować stronę poprzez wywołanie metody refresh().

Korzystanie ze zdarzeń frameworka WebDriver

Selenium WebDriver zawiera klasę EventFiringWebDriver, która nasłuchuje różnych zdarzeń zachodzących podczas wykonywania testu. Przykładowo zdarzenia są wyzwalane przy wejściu na stronę, gdy zostanie wykonane kliknięcie elementu albo kiedy zostanie zmieniona wartość. W poniższej tabeli zestawiono listę wszystkich zdarzeń, które możemy śledzić podczas wykonywania testu.

Zdarzenie

Opis

BeforeNavigateTo

Metoda jest wywoływana przed wywołaniem metod get(String url) lub navigate().to(String url).

afterNavigateTo

Metoda jest wywoływana po wywołaniu metod get(String url) lub navigate().to(String url).

beforeNavigateBack

Metoda jest wywoływana przed wywołaniem metody navigate().back().

afterNavigateBack

Metoda jest wywoływana po wywołaniu metody navigate().back().

beforeNavigateFor

Metoda jest wywoływana przed wywołaniem metody navigate().forward().

afterNavigateForward

Metoda jest wywoływana po wywołaniu metody navigate().forward().

beforeFindBy

Metoda jest wywoływana przed wywołaniem następujących metod:

WebDriver.findElement(...),

WebDriver.findElements(...),

WebElement.findElement(...),

WebElement.findElements(...).

afterFindBy

Metoda jest wywoływana po wywołaniu następujących metod:

WebDriver.findElement(...),

WebDriver.findElements(...),

WebElement.findElement(...),

WebElement.findElements(...).

beforeChangeValueOf

Metoda jest wywoływana przed wywołaniem metod WebElement.clear() lub WebElement.sendKeys(...).

afterChangeValueOf

Metoda jest wywoływana po wywołaniu metod WebElement.clear() lub WebElement.sendKeys(...).

beforeClickOn

Metoda jest wywoływana przed wywołaniem metody WebElement.click().

afterClickOn

Metoda jest wywoływana po wywołaniu metody WebElement.click().

beforeScript

Metoda jest wywoływana przed metodą RemoteWebDriver.executeScript(java.lang.String, java.lang.Object[]).

afterScript

Metoda jest wywoływana po metodzie RemoteWebDriver.executeScript(java.lang.String, java.lang.Object[]).

onException

Metoda ta jest wywoływana zawsze wtedy, gdy ma być zgłoszony wyjątek.

Wymienionych procedur obsługi zdarzeń można użyć do wykonania dodatkowych poleceń. Przykładowo przed wprowadzeniem wartości w polu tekstowym możemy wyczyścić istniejącą wartość lub przechwycić zrzut ekranu nawet wtedy, gdy WebDriver zgłosi wyjątek. Można to zrobić, wykonując następujące czynności.

Utwórz własną klasę nasłuchiwania zdarzeń. Do tej klasy możesz dodać kod, który zostanie uruchomiony po zgłoszeniu specyficznych zdarzeń.

Za pomocą egzemplarza WebDriver utwórz egzemplarz klasy EventFiringWebDriver.

Zarejestruj klasę słuchacza zdarzeń w egzemplarzu klasy EventFiringWebDriver.

Klasę słuchacza zdarzeń można utworzyć na dwa sposoby:

przez zaimplementowanie interfejsu WebDriverEventListener,

przez rozszerzenie klasy AbstractWebDriverEventListener.

W tej recepturze pokażę, w jaki sposób używać klasy EventFiringWebDriver do nasłuchiwania zdarzeń WebDriver.

Przygotuj się

Utwórz nowy test, który zbuduje egzemplarz WebDriver, przejdzie do witryny i wykona kilka podstawowych działań oraz weryfikacji.

Jak to zrobić?

Na początek zdefiniujemy klasę nasłuchiwania zdarzeń poprzez zaimplementowanie interfejsu WebDriverEventListener w następujący sposób.

package com.secookbook.examples.chapter04;

import org.openqa.selenium.*;

import org.openqa.selenium.support.events.WebDriverEventListener;

public class MyListener implements WebDriverEventListener {

    public void beforeChangeValueOf(WebElement element, WebDriver

driver) {

        element.clear();

    }

}

Następnie utworzymy test, który używa klasy EventFiringWebDriver.

package com.secookbook.examples.chapter04;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.support.events.EventFiringWebDriver;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

public class EventFiringTest {

    private WebDriver driver;

    @Before

    public void setUp() throws Exception {

        driver = new FirefoxDriver();

    }

    @Test

    public void testEventFiringWebDriver() throws Exception {

        EventFiringWebDriver eventDriver =

        new EventFiringWebDriver(driver);

        MyListener myListener = new MyListener();

        eventDriver.register(myListener);

        eventDriver.get("http://bit.ly/1DbdhsW");

        eventDriver.findElement(By.id("q"))

            .sendKeys("Selenium i testowanie aplikacji. Receptury");

    }

    @After

    public void tearDown() throws Exception {

        driver.quit();

    }

}

Co dalej?

Dodajemy do klasy odbiornika zdarzeń jedną dodatkową procedurę obsługi zdarzenia, która przechwytuje zrzut ekranu w momencie zgłaszania wyjątku.

public void onException(Throwable exception, WebDriver driver) {

    try {

    if

(driver.getClass().getName().equals("org.openqa.selenium.remote.Re

moteWebDriver")) {

    driver = new Augmenter().augment(driver);

    }

    File scrFile = ((TakesScreenshot)

driver).getScreenshotAs(OutputType.FILE);

    FileUtils.copyFile(scrFile, new

File("target/screenshots/error.png"));

    } catch (Exception e) {

      e.printStackTrace();

    }

}

Rozdział 5.
Synchronizacja testów

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Synchronizacja testów z niejawnym oczekiwaniem.

Synchronizacja testów z jawnym oczekiwaniem.

Synchronizacja testów z niestandardowymi warunkami oczekiwanymi.

Synchronizacja testów z wykorzystaniem klasy FluentWait.

Wprowadzenie

Kiedy budowane są automatyczne skrypty testujące złożone aplikacje webowe z wykorzystaniem Selenium WebDriver, trzeba zadbać o zachowanie właściwego przepływu, bo tego wymaga niezawodna automatyzacja testów.

Kiedy działają testy, aplikacja nie zawsze może odpowiadać z taką samą szybkością. Może na przykład upłynąć kilka sekund, zanim pasek postępu osiągnie 100%, wyświetli się komunikat o stanie, uaktywni przycisk czy też pokaże wyskakujące okno.

Spodziewane problemy z czasem można rozwiązać poprzez odpowiednie zsynchronizowanie testów. W ten sposób możemy zyskać pewność, że przed wykonaniem następnego kroku Selenium WebDriver będzie czekać, aż aplikacja osiągnie gotowość do jego wykonania. Istnieje kilka sposobów, które można zastosować do zsynchronizowania testu. W tym rozdziale zapoznasz się z różnymi funkcjami frameworka Selenium WebDriver umożliwiającymi implementację synchronizacji w testach.

Synchronizacja testów z niejawnym oczekiwaniem

Selenium WebDriver pozwala na synchronizowanie testów bazujące na niejawnym oczekiwaniu. Jeśli WebDriver nie może znaleźć elementu w modelu DOM, a w teście zaimplementowano niejawne oczekiwanie, to czeka przez zdefiniowany odcinek czasu, aż element pojawi się w DOM. Po upływie określonego czasu WebDriver ponowi próbę wyszukiwania elementu. Jeśli elementu nie znajdzie w ustalonym czasie, zostanie zgłoszony wyjątek NoSuchElementException.

Mówiąc inaczej, niejawne oczekiwanie polega na odpytywaniu modelu DOM przez określony odcinek czasu w celu odnalezienia elementu albo elementów, jeśli nie są dostępne natychmiast. Ustawienie domyślne to zero.

Jednorazowe ustawienie niejawnego oczekiwania powoduje, że obowiązuje ono przez cały czas życia egzemplarza obiektu WebDriver.

W tej recepturze krótko omówię użycie mechanizmu niejawnego oczekiwania. Zalecam jednak unikanie stosowania tego mechanizmu albo ograniczenie korzystania z niego do minimum.

Jak to zrobić?

Utwórzmy test dla przykładowej aplikacji AJAX zawierający następujący kod.

@Test

public void testWithImplicitWait() {

    WebDriver driver = new FirefoxDriver();

    // Uruchomienie przykładowej aplikacji Ajax

    driver.get("http://cookbook.seleniumacademy.com/AjaxDemo.html");

    // Ustawienie czasu niejawnego oczekiwania na 10 sekund

    driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

    try {

        // Uzyskanie łącza do strony 4 i kliknięcie go

        driver.findElement(By.linkText("Strona 4")).click();

        // Pobierz element o identyfikatorze page4 i sprawdź jego tekst

        WebElement message = driver.findElement(By.id("page4"));

        assertTrue(message.getText().contains("Nunc nibh tortor"));

    } finally {

        driver.quit();

    }

}

Jak to działa?

Selenium WebDriver udostępnia interfejs Timeouts służący do konfiguracji niejawnego oczekiwania. Interfejs Timeouts dostarcza metodę implicitlyWait(), która pobiera argument opisujący czas, przez jaki sterownik powinien czekać, gdy szuka elementu. W tym przykładzie, po pierwszej próbie sterownik będzie czekał na pojawienie się elementu w modelu DOM przez 10 sekund.

driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);

Do końca testu albo do ponownego ustawienia niejawnego oczekiwania na 0 test będzie czekać przez 10 sekund na pojawienie się elementu w modelu DOM każdorazowo podczas wyszukiwania elementu przy użyciu metody findElement().

Zastosowanie niejawnego oczekiwania może spowalniać działanie testów, gdy aplikacja reaguje normalnie, ponieważ test będzie czekać na pojawienie się każdego elementu w modelu DOM, co zwiększa całkowity czas wykonania testu. Należy zminimalizować użycie albo całkowicie wyeliminować korzystanie z niejawnego oczekiwania. Lepiej używać jawnego oczekiwania, które w porównaniu z oczekiwaniem niejawnym zapewnia większą kontrolę.

Zobacz też

Receptura „Synchronizacja testów z jawnym oczekiwaniem”,

Receptura „Synchronizacja testów z niestandardowymi warunkami oczekiwanymi”,

Receptura „Synchronizacja testów z wykorzystaniem klasy FluentWait”.

Synchronizacja testów z jawnym oczekiwaniem

Selenium WebDriver pozwala na stosowanie synchronizacji testów w trybie jawnego oczekiwania. Tryb ten zapewnia lepszy sposób czekania w porównaniu z oczekiwaniem niejawnym. Przy oczekiwaniu jawnym, w przeciwieństwie do oczekiwania niejawnego, można pisać oraz stosować predefiniowane lub niestandardowe warunki wznawiania działania kodu.

Selenium WebDriver dostarcza klasy WebDriverWait i ExpectedConditions, które implementują oczekiwanie jawne.

Klasa ExpectedConditions oferuje zbiór predefiniowanych warunków oczekiwania na wznowienie działania kodu. W poniższej tabeli zestawiono kilka warunków obsługiwanych przez klasę ExpectedConditions, które można często spotkać podczas automatyzowania przeglądarek internetowych.

Warunek predefiniowany

Metoda Selenium

Element jest widoczny i aktywny

elementToBeClickable(By locator)

Element jest zaznaczony

elementToBeSelected(WebElement element)

Obecność elementu

presenceOfElementLocated(By locator)

Element zawiera określony tekst

textToBePresentInElement(By locator, java.lang.String text)

Wartość elementu

textToBePresentInElementValue(By locator, java.lang.String text)

Tytuł

titleContains(java.lang.String title)

Informacje na temat dodatkowych warunków można znaleźć pod adresem http://seleniumhq.github.io/ selenium/docs/api/java/index.html.

W tej recepturze omówię kilka warunków dostępnych za pośrednictwem klasy WebDriverWait.

Jak to zrobić?

Spróbujmy wdrożyć test, który do zaimplementowania jawnego oczekiwania używa metody ExpectedConditions.titleContains().

@Test

public void testExplicitWaitTitleContains()

{

    // Przejdź do strony głównej Google

    WebDriver driver = new FirefoxDriver();

    driver.get("http://www.google.com");

    // Wprowadź termin do wyszukiwania i zatwierdź go

    WebElement query = driver.findElement(By.name("q"));

    query.sendKeys("selenium");

    query.click();

    // Utworzenie obiektu oczekiwania za pomocą klasy WebDriverWait.

    // Spowoduje to oczekiwanie przez maksymalnie 10 sekund przed aktualizacją tytułu

    // za pomocą terminu wyszukiwania.

    // Jeśli tytuł zostanie zaktualizowany w określonym czasie, to test przejdzie do następnego kroku

    // zamiast czekać przez 10 sekund

    WebDriverWait wait = new WebDriverWait(driver, 10);

    wait.until(ExpectedConditions.titleContains("selenium"));

    // Sprawdzenie tytułu

    assertTrue(driver.getTitle().toLowerCase().startsWith("selenium"));

    driver.quit();

}

Jak to działa?

Za pomocą klasy ExpectedConditions możemy zdefiniować jawne oczekiwanie z wykorzystaniem zbioru powszechnie stosowanych warunków. Na początek trzeba utworzyć egzemplarz klasy WebDriverWait poprzez przekazanie obiektu driver oraz limitu czasu oczekiwania. Można to zrobić w następujący sposób.

WebDriverWait wait = new WebDriverWait(driver, 10);

Teraz można przekazać do metody wait.until() obiekt ExpectedCondition w poniższy sposób.

wait.until(ExpectedConditions.titleContains("selenium"));

Obiekt WebDriverWait co 500 milisekund będzie wywoływał obiekt klasy ExpectedConditions do czasu, aż pomyślnie zwróci sterowanie.

Zobacz też

Receptura „Synchronizacja testów z niejawnym oczekiwaniem”,

Receptura „Synchronizacja testów z niestandardowymi warunkami oczekiwanymi”,

Receptura „Synchronizacja testów z wykorzystaniem klasy FluentWait”.

Synchronizacja testów z niestandardowymi warunkami oczekiwanymi

Posługując się klasą ExpectedCondition razem z warunkami standardowymi, można również tworzyć warunki niestandardowe. Własność ta przydaje się w sytuacji, kiedy nie można obsłużyć oczekiwania za pomocą typowego warunku dostarczanego przez klasę ExpectedCondition.

W tej recepturze pokażę sposób tworzenia takich niestandardowych warunków.

Jak to zrobić?

Utworzymy test, który będzie czekał na wyświetlenie elementu na stronie; posłużymy się klasą ExpectedCondition w następujący sposób.

@Test

public void testExplicitWait() {

    WebDriver driver = new FirefoxDriver();

    // Uruchomienie przykładowej aplikacji Ajax

    driver.get("http://cookbook.seleniumacademy.com/AjaxDemo.html");

    try {

        driver.findElement(By.linkText("Strona 4")).click();

        WebElement message = new WebDriverWait(driver, 5)

            .until(new ExpectedCondition<WebElement>() {

                public WebElement apply(WebDriver d) {

                    return d.findElement(By.id("page4"));

                }

            });

        assertTrue(message.getText().contains("Nunc nibh tortor"));

    } finally {

        driver.quit();

    }

}

Jak to działa?

Selenium WebDriver zapewnia możliwość niestandardowej implementacji interfejsu ExpectedCondition razem z klasą WebDriverWait. Pozwala to na utworzenie niestandardowych warunków oczekiwania zgodnie z potrzebami testu. W tym przykładzie napisano niestandardowy warunek, który zwraca obiekt WebElement po zlokalizowaniu go, w ramach ustalonego limitu czasu, przez wewnętrzną metodę findElement().

WebElement message = new WebDriverWait(driver, 5)

.until(new ExpectedCondition<WebElement>(){

    @Override

    public WebElement apply(WebDriver d) {

    return d.findElement(By.id("page4"));

}});

Co dalej?

Niestandardowe warunki oczekiwania mogą być tworzone na różne sposoby. W poniższych podpunktach omówiono kilka typowych przykładów implementacji niestandardowego oczekiwania.

Oczekiwanie na aktualizację wartości atrybutu elementu

W odpowiedzi na zdarzenia i wykonywane działania wartość atrybutu elementu może zmieniać się w czasie wykonywania aplikacji. Przykładowo na podstawie uprawnień użytkownika nieaktywne pole tekstowe może się uaktywnić. Niestandardowe obiekty oczekiwania można tworzyć na podstawie wartości atrybutu elementu. W poniższym przykładzie obiekt klasy ExpectedCondition implementuje oczekiwanie na zwrócenie wartości Boolean na podstawie wartości atrybutu elementu.

new WebDriverWait(driver, 10).until(new

ExpectedCondition<Boolean>() {

    public Boolean apply(WebDriver d) {

        return

d.findElement(By.id("userName")).getAttribute("readonly").contains

("true");

}});

Oczekiwanie na widoczność elementu

Programiści mogą ukrywać lub wyświetlać elementy na podstawie sekwencji działań użytkownika, uprawnień i tak dalej. Określony element może istnieć w modelu DOM, ale jest ukryty przed użytkownikiem i wyświetla się na stronie w odpowiedzi na określone działania. Niestandardowy warunek oczekiwania na podstawie widoczności elementu można utworzyć w następujący sposób.

new WebDriverWait(driver, 10).until(new

ExpectedCondition<Boolean>() {

    public Boolean apply(WebDriver d) {

        return d.findElement(By.id("page4")).isDisplayed();

}});

Oczekiwanie na zdarzenia w modelu DOM

Do manipulowania zawartością i do obsługi technik AJAX aplikacja webowa może używać frameworka języka JavaScript, na przykład biblioteki jQuery. I tak wywołania jQuery mogą być użyte do asynchronicznego załadowania na stronie dużego pliku JSON z serwera. Podczas gdy jQuery czyta i przetwarza ten plik, test może sprawdzić jego status za pomocą atrybutu active. Niestandardowe oczekiwanie może być zaimplementowane poprzez uruchomienie kodu JavaScript i sprawdzenie zwracanej wartości w następujący sposób.

new WebDriverWait(driver, 10).until(new

ExpectedCondition<Boolean>() {

    public Boolean apply(WebDriver d) {

        JavascriptExecutor js = (JavascriptExecutor) d;

        return (Boolean)js.executeScript("return jQuery.active ==

0");

}});

Zobacz też

Receptura „Synchronizacja testów z niejawnym oczekiwaniem”,

Receptura „Synchronizacja testów z jawnym oczekiwaniem”,

Receptura „Synchronizacja testów z wykorzystaniem klasy FluentWait”.

Synchronizacja testów z wykorzystaniem klasy FluentWait

Klasa FluentWait jest implementacją interfejsu Wait frameworka Selenium WebDriver. Za pomocą klasy FluentWait możemy zdefiniować maksymalną ilość czasu oczekiwania na element lub warunek, a także częstotliwość sprawdzania warunku. Można również podczas wyszukiwania elementu skonﬁgurować specyficzne typy wyjątków, takie jak wyjątek NoSuchElementException.

W przeciwieństwie do oczekiwania niejawnego i jawnego, przy oczekiwaniu bazującym na klasie FluentWait używane są wartości maksymalnego timeoutu i częstotliwości odpytywania. Jeśli na przykład ustawimy wartość maksymalnego timeoutu na 20 sekund, a częstotliwość odpytywania na 2 sekundy, WebDriver będzie sprawdzał element co 2 sekundy, aż do osiągnięcia maksymalnej wartości. Oprócz tego możemy skonfigurować obiekt klasy FluentWait tak, aby podczas oczekiwania na spełnienie warunku ignorował specyficzne typy wyjątków.

Klasa FluentWait przydaje się do automatyzacji aplikacji AJAX lub w przypadku scenariuszy, w których czas ładowania elementu często się zmienia.

W tej recepturze omówię niektóre z warunków dostępnych za pośrednictwem klasy FluentWait.

Jak to zrobić?

Spróbujmy zaimplementować test, który używa klasy FluentWait do znalezienia i oczekiwania na element, tak jak pokazano w poniższym przykładzie.

@Test

public void testFluentWait() {

    WebDriver driver = new ChromeDriver();

    // Uruchomienie przykładowej aplikacji Ajax

    driver.get("http://cookbook.seleniumacademy.com/AjaxDemo.html");

    try {

        driver.findElement(By.linkText("Strona 4")).click();

        Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)

            .withTimeout(10, TimeUnit.SECONDS)

            .pollingEvery(2, TimeUnit.SECONDS)

            .ignoring(NoSuchElementException.class);

        WebElement message = wait

            .until(new Function<WebDriver, WebElement>() {

                public WebElement apply(WebDriver d) {

                    return d.findElement(By.id("page4"));

                }

            });

        assertTrue(message.getText().contains("Nunc nibh tortor"));

    } finally {

        driver.quit();

    }

}

Jak to działa?

Za pomocą klasy FluentWait możemy zdefiniować obiekt płynnego oczekiwania. Możemy skonfigurować maksymalny limit czasu na 10 sekund, częstotliwość odpytywania co 2 sekundy oraz ignorowanie wyjątków poprzez wywołanie metod klasy FluentWait, tak jak pokazano w poniższym przykładzie.

Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)

    .withTimeout(10, TimeUnit.SECONDS)

    .pollingEvery(2, TimeUnit.SECONDS)

        .ignoring(NoSuchElementException.class);

Gdy użyjemy tego egzemplarza w celu wyszukania elementu, WebDriver będzie czekać przez 10 sekund, odpytując co 2 sekundy wskazany element modelu DOM.

WebElement message = wait

    .until(new Function<WebDriver, WebElement>(){

        public WebElement apply(WebDriver d) {

            return d.findElement(By.id("page4"));

        }});

W tym przykładzie szukamy elementu za pomocą obiektu klasy FluentWait, a po jego znalezieniu zwracamy go do testu.

Co dalej?

W przeciwieństwie do poprzedniego przykładu, w którym zwracaliśmy element, w tym przykładzie możemy również czekać na spełnienie określonych warunków. W celu obsługi warunków oczekiwania klasę FluentWait można skonﬁgurować na wiele sposobów, co pokazano w następującym przykładowym kodzie.

@Test

public void testFluentWaitWithPredicate() {

    final WebDriver driver = new ChromeDriver();

    // Uruchomienie przykładowej aplikacji Ajax

    driver.get("http://cookbook.seleniumacademy.com/AjaxDemo.html");

    try {

        FluentWait<By> wait = new FluentWait<By>(By.linkText("Strona 4"))

            .withTimeout (1000, TimeUnit.MILLISECONDS)

            .pollingEvery(200, TimeUnit.MILLISECONDS)

            .ignoring(NoSuchElementException.class);

        wait.until(new Predicate<By>() {

            public boolean apply(By by) {

                try {

                    return driver.findElement(by).isDisplayed();

                } catch (NoSuchElementException ex) {

                    return false;

                }

            }

        });

        driver.findElement(By.linkText("Strona 4")).click();

    } finally {

        driver.quit();

    }

}

Zobacz też

Receptura „Synchronizacja testów z niejawnym oczekiwaniem”,

Receptura „Synchronizacja testów z jawnym oczekiwaniem”,

Receptura „Synchronizacja testów z niestandardowymi warunkami oczekiwanymi”.

Rozdział 6.
Alerty, ramki i okna

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Obsługa prostych okien alertów JavaScript.

Obsługa okien ostrzeżeń confirm i prompt.

Identyfikowanie i obsługa ramek.

Ramki IFRAME.

Identyfikowanie i obsługa okna potomnego.

Identyfikowanie i obsługa okien na podstawie tytułu.

Identyfikowanie i obsługa wyskakujących okien na podstawie zawartości.

Wprowadzenie

W celu opracowania rozbudowanych interfejsów użytkownika programiści korzystają z własności podobnych do tych, które są stosowane w aplikacjach desktopowych — to znaczy z wyskakujących okien i alertów. Podczas testowania złożonych przepływów w testach należy przejść z okna przeglądarki do wyskakującego okna, aby ostrzec użytkownika i wykonać określone operacje, jeśli będzie trzeba. W tym rozdziale wyjaśnię typowe problemy związane z obsługą wyskakujących okien i alertów oraz opowiem, jak do tego celu najlepiej wykorzystać API frameworka Selenium WebDriver.

Obsługa prostych okien alertów JavaScript

Programiści aplikacji webowych korzystają z alertów JavaScript do informowania użytkowników o błędach sprawdzania poprawności danych, ostrzeżeniach, a także do pobierania odpowiedzi na działania, danych wejściowych i tak dalej. Alerty są wyświetlanymi przez przeglądarki oknami modalnymi, w których użytkownik musi podjąć określone działania przed kontynuowaniem pracy z aplikacją. Więcej informacji na temat metody alert() języka JavaScript można znaleźć pod adresem http://www.w3schools.com/jsref/met_win_alert.asp.

Alerty JavaScript są implementowane w przeglądarkach inaczej niż inne okna dialogowe, takie jak Drukuj, Zapisz, Pobierz plik i tak dalej.

Podczas testowania trzeba zweryfikować, czy są wyświetlane odpowiednie alerty. Obsługa przepływu „od końca do końca” wymaga również obsłużenia alertów. Framework Selenium WebDriver do obsługi alertów JavaScript dostarcza interfejs Alert.

W tej recepturze zautomatyzuję interakcje z prostym oknem alertu, które jest często używane do powiadamiania użytkownika o takich informacjach jak błędy, ostrzeżenia czy też powodzenie operacji. Gdy wyświetli się okno alertu, użytkownik, aby kontynuować pracę, będzie musiał kliknąć przycisk OK, tak jak pokazano na poniższym zrzucie ekranu.

Jak to zrobić?

W tej recepturze wykorzystamy prostą stronę, na której, gdy użytkownik kliknie przycisk, wyświetla się proste okno alertu. Utwórzmy test, który sprawdza, czy w oknie alertu wyświetlają się odpowiednie informacje, tak jak pokazano w następującym przykładowym kodzie.

package com.secookbook.examples.chapter06;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.support.ui.ExpectedConditions;

import org.openqa.selenium.support.ui.WebDriverWait;

import org.openqa.selenium.By;

import org.openqa.selenium.Alert;

import org.junit.AfterClass;

import org.junit.BeforeClass;

import org.junit.Test;

import static org.junit.Assert.*;

public class AlertsTest {

    private static WebDriver driver;

    @BeforeClass

    public static void setUp() {

        driver = new FirefoxDriver();

        driver.get("http://cookbook.seleniumacademy.com/Alerts.html");

        driver.manage().window().maximize();

    }

    @Test

    public void testSimpleAlert() {

        // Kliknięcie przycisku w celu wyświetlenia okna alertu

        driver.findElement(By.id("simple")).click();

        // Opcjonalnie można również czekać na wyświetlenie okna alertu za pomocą

        // obiektu klasy WebDriverWait

        new WebDriverWait(driver, 10)

            .until(ExpectedConditions.alertIsPresent());

        // Pobranie alertu

        Alert alert = driver.switchTo().alert();

        // Pobranie tekstu wyświetlanego w oknie alertu

        String textOnAlert = alert.getText();

        // Sprawdzenie, czy w oknie alertu wyświetla się prawidłowy komunikat

        assertEquals("Witaj! Jestem w oknie alertu! ", textOnAlert);

        // Kliknięcie przycisku OK poprzez wywołanie metody accept

        alert.accept();

}

    @AfterClass

    public static void tearDown() {

        driver.quit();

    }

}

Jak to działa?

Framework Selenium WebDriver dostarcza interfejs Alert do obsługi alertów. Interfejs ten oferuje różne metody interakcji z oknem alertu. Metoda driver.switchTo().alert() zwraca egzemplarz klasy Alert reprezentujący okno alertu wyświetlane na ekranie. Można z niej skorzystać w następujący sposób.

Alert alert = driver.switchTo().alert();

Czasami trzeba sprawdzić, jaki komunikat jest wyświetlany w oknie alertu. Aby uzyskać tekst z okna alertu, można wywołać metodę getText() dostarczaną przez interfejs Alert. Robimy to w następujący sposób.

String textOnAlert = alert.getText();

Zamknięcie okna następuje po kliknięciu przycisku OK. Można to zrobić przez wywołanie metody accept() w następujący sposób.

alert.accept();

Do okna alertu można również uzyskać dostęp, bezpośrednio wywołując żądaną metodę. Przykład pokazano w poniższym kodzie.

driver.switchTo().alert().accept();

Co dalej?

Za pomocą klasy WebDriverWait i metody alertIsPresent() klasy ExpectedConditions możemy także utworzyć jawny warunek oczekiwania, tak jak pokazano w poniższym kodzie.

new WebDriverWait(driver, 10)

    .until(ExpectedConditions.alertIsPresent());

// Uzyskanie alertu

Alert alert = driver.switchTo().alert();

Jest to przydatne, w sytuacji gdy istnieje opóźnienie pomiędzy działaniami wykonywanymi przez użytkownika a wyświetleniem okna alertu.

Wyjątek NoAlertPresentException

Metoda driver.switchTo().alert() w wyniku próby dostępu do okna alertu, które nie istnieje, zgłasza wyjątek NoAlertPresentException.

Zobacz też

Receptura „Obsługa okien confirm i prompt”.

Obsługa okien confirm i prompt

Okno confirm jest często używane do weryfikacji lub akceptacji danych wprowadzanych przez użytkownika. Aby po wyświetleniu alertu confirm można było kontynuować pracę z aplikacją, użytkownik musi kliknąć przycisk OK lub Anuluj, tak jak pokazano na poniższym zrzucie ekranu.

Jeśli użytkownik kliknie OK, okno confirm zwraca odpowiedź w postaci wartości true. Gdy użytkownik kliknie przycisk Anuluj, zwraca false.

Okno prompt

Okno prompt jest często używane do przyjmowania danych wejściowych od użytkownika. Gdy wyświetli się okno prompt, użytkownik, aby kontynuować, będzie musiał wprowadzić wartość i kliknąć przycisk OK lub Anuluj, tak jak pokazano na poniższym zrzucie ekranu.

Jeśli użytkownik kliknie przycisk OK, funkcja zwróci wprowadzoną wartość. Kiedy użytkownik kliknie Anuluj, funkcja zwróci null.

W tej recepturze obsłużę okna confirm i prompt przy użyciu interfejsu Alert frameworka Selenium WebDriver.

Jak to zrobić?

Utwórzmy zestaw testów, które obsługują wyświetlane na stronie okno confirm, w następujący sposób.

W teście testConfirmAccept zaakceptujemy pole confirm i zweryfikujemy komunikat na stronie, gdy pole to zostanie zaakceptowane — to znaczy kiedy użytkownik kliknie przycisk OK, tak jak pokazano w poniższym kodzie.

@Test

    Public void testConfirmAccept() {}

        // Kliknięcie przycisku Potwierdź w celu wyświetlenia okna alertu potwierdzenia

        driver.findElement(By.id("confirm")).click();

        // Uzyskanie reprezentacji okna z alertem

        Alert alert = driver.switchTo().alert();

        // Kliknięcie przycisku OK poprzez wywołanie metody accept

        alert.accept();

        // Sprawdzenie, czy na stronie wyświetla się prawidłowy komunikat

        WebElement message = driver.findElement(By.id("demo"));

        assertEquals("Zaakceptowałeś alert!", message.getText());

}

W teście testConfirmDismiss zamkniemy okno confirm przez wywołanie dismiss(), co odpowiada kliknięciu przycisku Anuluj, tak jak pokazano w poniższym kodzie.

@Test

    Public void testConfirmDismiss() {}

        // Kliknięcie przycisku Potwierdź, aby wyświetlić okno alertu potwierdzenia

        driver.findElement(By.id("confirm")).click();

        // Uzyskanie reprezentacji okna z alertem

        Alert alert = driver.switchTo().alert();

        // Kliknięcie przycisku Anuluj poprzez wywołanie metody dismiss

        alert.dismiss();

        // Sprawdzenie, czy na stronie wyświetla się prawidłowy komunikat

        WebElement message = driver.findElement(By.id("demo"));

        assertEquals("Anulowałeś alert!", message.getText());

}

Obsługa okna prompt

Utwórzmy test, który obsługuje okno prompt. W polu prompt wprowadzimy tekst, a później zweryfikujemy, czy na stronie jest wyświetlana taka sama wartość — tak jak pokazano w poniższym przykładzie kodu.

@Test

    public void testPrompt() {

        // Kliknięcie przycisku Potwierdź w celu wyświetlenia okna prompt

        driver.findElement(By.id("prompt")).click();

        // Uzyskanie reprezentacji okna z alertem

        Alert alert = driver.switchTo().alert();

        // Wprowadzenie wartości w oknie prompt

        alert.sendKeys("Foo");

        // Kliknięcie przycisku OK poprzez wywołanie metody accept

        alert.accept();

        // Sprawdzenie, czy na stronie wyświetla się komunikat z wartością wprowadzoną w oknie prompt

        WebElement message = driver.findElement(By.id("prompt_demo"));

        assertEquals("Witam Foo! Jak się dzisiaj miewasz?",

        message.getText());

}

Jak to działa?

Obsługa okna confirm przypomina obsługę prostego okna alert. Do anulowania okna confirm służy metoda dismiss() interfejsu Alert, którą można wykorzystać w następujący sposób.

alert.dismiss();

Do obsługi okna prompt interfejs Alert dostarcza dodatkową metodę sendKeys(), która umożliwia wprowadzanie tekstu w wejściowym polu tekstowym okna prompt. Możemy wpisać tekst i zaakceptować lub anulować okno prompt, posługując się metodą interfejsu Alert w następujący sposób.

alert.sendKeys("Foo");

alert.accept();

Zobacz też

Receptura „Obsługa prostych okien alertów JavaScript”.

Identyfikowanie i obsługa ramek

Ramki HTML pozwalają programistom na prezentowanie dokumentów w wielu widokach, które mogą wyświetlać się w osobnym oknie potomnym albo w oknie podrzędnym. Możliwość posługiwania się wieloma widokami daje programistom sposobność zapewnienia widoczności określonych informacji w czasie, kiedy inne widoki są przewijane lub zastępowane. Przykładowo w tym samym oknie, w jednej ramce może się wyświetlać statyczny baner, w drugiej menu nawigacji, a w trzeciej dokument główny, który można przewijać lub zastępować poprzez nawigowanie wewnątrz drugiej ramki.

Stronę z ramkami tworzy się za pomocą znacznika <frameset> lub <iframe>. Wszystkie tagi <frame> są zagnieżdżone wewnątrz taga <frameset>. W poniższym przykładzie na stronie wyświetlają się trzy ramki. Każda z nich odpowiada za załadowanie różnych stron HTML.

<html>

    <frameset cols="25%,*,25%" frameborder="NO" framespacing="0" border="0">

        <frame id="left" src="frame_a.htm" />

        <frame src="frame_b.htm" />

        <frame name="right" src="frame_c.htm" />

    </frameset>

</html>

Ramki mogą być identyfikowane za pośrednictwem identyfikatora lub za pomocą atrybutu name. W tej recepturze zidentyfikuję ramki i będę z nimi pracować przy użyciu metody driver.switchTo().frame() interfejsu WebDriver.TargetLocator, który służy do zlokalizowania ramki bądź okna na podstawie atrybutów id i name, egzemplarza obiektu WebElement i indeksu.

Jak to zrobić?

Utwórzmy test na prostej stronie składającej się z trzech ramek. Do zidentyfikowania tych ramek i interakcji z ich zawartością użyjemy atrybutów id, name i indeksu, tak jak pokazano w poniższych przykładach.

W teście testFrameWithIdOrName do identyfikacji ramek użyjemy atrybutów name i id, tak jak pokazano w poniższym kodzie.

package com.secookbook.examples.chapter06;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.By;

import org.openqa.selenium.WebElement;

import org.junit.AfterClass;

import org.junit.BeforeClass;

import org.junit.Test;

import static org.junit.Assert.*;

public class FramesTest {

    public static WebDriver driver;

    @BeforeClass

    public static void setUp() {

        driver = new FirefoxDriver();

        driver.get("http://cookbook.seleniumacademy.com/Frames.html");

        driver.manage().window().maximize();

    }

    @Test

    public void testFrameWithIdOrName() {

        try {

            // Uaktywnienie ramki po lewej stronie za pomocą jej atrybutu id

            driver.switchTo().frame("left");

            // Pobranie elementu z ramki po lewej stronie i zweryfikowanie jego

            // zawartości

            WebElement msg = driver.findElement(By.tagName("p"));

            assertEquals("To jest ramka po lewej", msg.getText());

    } finally {

        // Uaktywnienie strony. Spowoduje to przeniesienie kontekstu z ramki

        // z powrotem na stronę

        driver.switchTo().defaultContent();

    }

    try {

        // Uaktywnienie ramki po prawej stronie przy użyciu jej atrybutu name

        driver.switchTo().frame("right");

        // Pobranie elementu z ramki po prawej stronie i zweryfikowanie jego

        // zawartości

        WebElement msg = driver.findElement(By.tagName("p"));

        assertEquals ("To jest ramka po prawej", msg.getText());

    } finally {

        // Uaktywnienie strony. Spowoduje to przeniesienie kontekstu z ramki

        // z powrotem na stronę

        driver.switchTo().defaultContent();

        }

    }

    @AfterClass

    public static void tearDown() {

        // Zamknięcie nadrzędnego okna popup

        driver.close();

        driver.quit();

    }

}

W drugim teście, czyli testFrameByIndex, do zidentyfikowania ramki użyjemy indeksu, tak jak pokazano w poniższym kodzie.

@Test

public void testFrameByIndex() {

    try {

        // Uaktywnienie środkowej ramki przy użyciu jej indeksu. Indeks zaczyna się od 0

        driver.switchTo().frame(1);

        // Pobranie elementu odpowiadającego środkowej ramce i zweryfikowanie jego

        // zawartości

        WebElement msg = driver.findElement(By.tagName("p"));

        assertEquals("Ta ramka nie ma identyfikatora ani nazwy", msg.

getText());

    } finally {

        // Uaktywnienie strony. To przeniesie kontekst z ramki

        // z powrotem na stronę

        driver.switchTo().defaultContent();

    }

}

Jak to działa?

Interfejs WebDriver.TargetLocator frameworka Selenium WebDriver dostarcza metodę driver.switchTo().frame(), która jest odpowiedzialna za uaktywnienie ramki na stronie i wykonanie na niej operacji. Metoda ta pobiera wartości atrybutów id lub name albo egzemplarz obiektu WebElement (element <frame> można zlokalizować za pomocą metody driver.findElement()).

W poniższym przykładzie do zidentyfikowania ramki użyto atrybutu id.

// Uaktywnienie ramki po lewej stronie za pomocą atrybutu id

driver.switchTo().frame("left");

Gdy ramka nie ma zdefiniowanych atrybutów id ani name, do jej zidentyfikowania można skorzystać z indeksu. W poprzednim przykładzie środkowa ramka nie ma atrybutów id ani name. Indeks środkowej ramki ma wartość 1. Ramka po lewej stronie ma indeks 0, a po prawej stronie indeks 2.

// Uaktywnienie środkowej ramki przy użyciu jej indeksu. Indeks zaczyna się od 0

driver.switchTo().frame(1);

Po uaktywnieniu ramki możemy pozostawić egzemplarz driver w celu interakcji ze stroną, którą załadowano w tej ramce. Aby powrócić do strony głównej, należy użyć metody driver.switchTo().defaultContent().

Ostrzeżenie: Jeśli pracujemy z wieloma ramkami, a po zakończeniu operacji w ramce sterowanie testu musi przenieść się do innej ramki, wywołanie metody driver.switchTo().frame() nie przeniesie kontekstu do żądanej ramki. Test musi najpierw aktywować stronę główną poprzez wywołanie metody driver.switchTo().defaultContent(), a dopiero potem uaktywnić żądaną ramkę.

Co dalej?

Podczas pracy z ramkami może się zdarzyć, że atrybuty id lub name nie zostały zdefiniowane, mimo to ramki mogą być identyfikowane za pomocą ich indeksu. Ten sposób może być zawodny, gdy aplikacje są dynamiczne i istnieje konieczność uaktywnienia odpowiedniej ramki.

Przyjrzyjmy się przykładowi, w którym będziemy identyfikować ramki na podstawie zawartości strony załadowanej w tych ramkach. Wtedy testy staną się bardziej wiarygodne.

@Test

public void testFrameByContents() {

    // Utworzenie reprezentacji wszystkich ramek na stronie zbudowanych z wykorzystaniem znacznika <frame>

    List<WebElement> frames = driver.findElements(By.tagName("frame"));

    // W tym przykładzie środkowa ramka jest aktywowana poprzez sprawdzenie jej zawartości.

    // Uaktywnienie ramki i sprawdzenie, czy zawiera żądaną zawartość. Jeśli zostanie znaleziona,

    // to operacje będą wykonane.

    // Jeśli nie, nastąpi przejście do strony i kontynuowanie sprawdzania następnej ramki

    try {

        for (WebElement frame : frames) {

            // Metoda switchTo().frame() oprócz atrybutu name, id i indeksu akceptuje również elementy frame

            driver.switchTo().frame(frame);

            String title = driver.getTitle();

            if (title.equals("Ramka B")) {

                WebElement msg = driver.findElement(By.tagName("p"));

                assertEquals("To jest ramka po lewej", msg.getText());

                break;

            } else

                driver.switchTo().defaultContent();

        }

    } finally {

        // Uaktywnienie strony. To przeniesie kontekst z ramki

        // z powrotem na stronę

        driver.switchTo().defaultContent();

    }

}

We frameworku Selenium WebDriver możemy uzyskać listę wielu elementów spełniających te same kryteria. W prezentowanym przykładzie uzyskamy wszystkie elementy ramki ze strony przy użyciu metody tagName(), tak jak pokazano poniżej.

// Utworzenie reprezentacji wszystkich ramek na stronie zbudowanych z wykorzystaniem znacznika <frame>

List<WebElement> frames = driver.findElements(By.tagName("frame"));

Test będzie iterował po wszystkich elementach frame, przekazywał je do metody driver.switchTo().frame(), a następnie sprawdzał ich zawartość. Jeżeli ramka zawiera pasującą zawartość, możemy kontynuować operacje na ramce, a następnie przełączyć się do strony głównej, tak jak pokazano w poniższym kodzie.

for (WebElement frame : frames) {

        // Metoda switchTo().frame() oprócz atrybutu name, id i indeksu akceptuje również elementy frame

        driver.switchTo().frame(frame);

        String title = driver.getTitle();

        if (title.equals("Ramka B")) {

            WebElement msg = driver.findElement(By.tagName("p"));

            assertEquals("To jest ramka po lewej", msg.getText());

            break;

    } else

        driver.switchTo().defaultContent();

}

Można utworzyć metodę narzędziową odpowiedzialną za przełączanie pomiędzy ramkami na podstawie ich zawartości.

Zobacz też

Receptura „Ramki iframe”.

Ramki iframe

Za pośrednictwem znacznika <iframe> udostępniającego tak zwane ramki inline programiści mogą osadzać na stronie dokumenty z zewnątrz. Media społecznościowe dostarczają przyciski, które mogą być osadzane w aplikacjach webowych w celu zapewnienia łączy do tych witryn internetowych. Przykładowo przycisk do śledzenia Twittera można osadzić w aplikacji w następujący sposób.

<iframe allowtransparency="true" frameborder="0" scrolling="no"

src="http://platform.twitter.com/widgets/follow_button.html?screen

_name=upgundecha" style="width:300px; height:20px;"></iframe>

Identyfikowanie i obsługa znacznika <iframe> przypominają obsługę znacznika frame tworzonego za pomocą znacznika <frameset>. W tej recepturze zidentyfikuję tag <iframe>, który jest zagnieżdżony w innej ramce.

Jak to zrobić?

Na przykładowej stronie utworzymy test, który osadza znacznik <iframe> wewnątrz ramki. Najpierw przejdziemy do ramki nadrzędnej, a następnie do elementu <iframe>. Test kliknie element wewnątrz taga <iframe> i wykona działania w aktualnie wyświetlanym oknie popup. Na koniec przełączy się do dokumentu głównego. Te działania zaimplementowano w poniższym przykładowym kodzie.

@Test

Public void testIFrame() {}

    // Przechowanie uchwytu do bieżącego okna sterownika

    String currentWindow = driver.getWindowHandle();

    // Ramka po prawej stronie zawiera zagnieżdżony element iframe z przyciskiem

    // śledzenia Twittera.

    // Uaktywnienie ramki po prawej stronie przy użyciu jej atrybutu name

try {

        driver.switchTo().frame("right");

        // Uzyskanie reprezentacji elementu iframe

        WebElement twitterFrame = driver.findElement(By.tagName("iframe"));

        try {

            // Uaktywnienie elementu iframe

            driver.switchTo().frame(twitterFrame);

            // Uzyskanie reprezentacji przycisku śledzenia Twittera z elementu iframe i kliknięcie go.

            // Po kliknięciu wyświetli się okno popup

            WebElement button = driver.findElement(By.id("follow-button"));

            button.click();

            try {

                // Okno popup Twittera nie ma atrybutów name ani title.

                // Skrypt uzyska uchwyty wszystkich otwartych okien.

                // Żądane okno zostanie aktywowane na podstawie sprawdzenia jego tytułu

                for (String windowId : driver.getWindowHandles()) {

                    driver.switchTo().window(windowId);

                    if (driver.getTitle().equals(

                          "Unmesh Gundecha (@upgundecha) on Twitter")) {

                        assertTrue("Znaleziono okno popup logowania do Twittera", true);

                        driver.close();

                        break;

                   }

                }

            } finally {

                // Przełączenie się do oryginalnego okna sterownika

                driver.switchTo().window(currentWindow);

            }

        } finally {

            // Przełączenie się do strony z ramki

            driver.switchTo().defaultContent();

        }

    } finally {

        // Przełączenie się do strony z ramki

        driver.switchTo().defaultContent();

    }

}

Jak to działa?

Obsługa ramek inline przypomina obsługę normalnych ramek. W tym przykładzie element <iframe> zlokalizowano za pomocą metody findElement() poprzez przekazanie do niej nazwy taga.

WebElement twitterFrame =

driver.findElement(By.tagName("iframe"));

Aby uaktywnić ramkę, wywołano metodę driver.switchTo().frame(). W tym celu przekazano do niej egzemplarz obiektu WebElement w następujący sposób.

driver.switchTo().frame(twitterFrame)

Zobacz też

Receptura „Identyfikowanie i obsługa ramek”.

Identyfikowanie i obsługa okien potomnych

W Selenium WebDriver testowanie wielu okien obejmuje zidentyfikowanie okna, przełączenie kontekstu sterownika do okna, następnie wykonanie działań w oknie i — na koniec — przełączenie się z powrotem do przeglądarki.

Selenium WebDriver umożliwia identyfikację okna na podstawie jego atrybutu name lub uchwytu do okna, natomiast przełączanie się pomiędzy oknem a oknem przeglądarki można wykonać za pomocą metody WebDriver.switchTo().window() interfejsu WebDriver.TargetLocator.

W tej recepturze zidentyfikuję okno na podstawie atrybutu name. Programiści dostarczają dla okna atrybut name, który ma wartość inną niż tytuł okna. W poniższym przykładzie użytkownik może otworzyć okno, klikając przycisk Pomoc. W tym przypadku programista zdefiniował nazwę okna jako HelpWindow.

<button id="helpbutton" on

Click='window.open("help.html","HelpWindow","width=500,height=500"

);'>Pomoc</button>

Jak to zrobić?

Utwórzmy test, w którym zidentyfikujemy okno na podstawie jego atrybutu name, tak jak pokazano w poniższym kodzie.

package com.secookbook.examples.chapter06;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.By;

import static org.junit.Assert.*;

import org.junit.AfterClass;

import org.junit.BeforeClass;

import org.junit.Test;

public class WindowTest {

    public static WebDriver driver;

    @BeforeClass

    public static void setUp() {

        driver = new FirefoxDriver();

        driver.get("http://cookbook.seleniumacademy.com/Config.html");

        driver.manage().window().maximize();

    }

    @Test

    public void testWindowUsingName() {

        // Zapamiętanie uchwytu WindowHandle nadrzędnego okna przeglądarki

        String parentWindowId = driver.getWindowHandle();

        // Kliknięcie przycisku Pomoc otworzy stronę pomocy w nowym oknie potomnym

        driver.findElement(By.id("helpbutton")).click();

        try {

            // Przełączenie się do okna pomocy przy użyciu jego nazwy

            driver.switchTo().window("HelpWindow");

    try {

        // Sprawdzenie, czy kontekstem sterownika jest okno pomocy

        assertEquals("Pomoc", driver.getTitle());

    } finally {

        // Zamknięcie okna pomocy

        driver.close();

        }

    } finally {

        // Przełączenie się do nadrzędnego okna przeglądarki

        driver.switchTo().window(parentWindowId);

    }

    // Sprawdzenie, czy kontekstem sterownika jest nadrzędne okno przeglądarki

    assertEquals("Zbuduj swój samochód - Konfiguracja",

    driver.getTitle());

    }

    @AfterClass

    public static void tearDown() {

        driver.quit();

    }

}

Jak to działa?

Selenium WebDriver umożliwia przełączanie się pomiędzy przeglądarką a oknami i zmianę kontekstu sterownika. Aby przejść do okna podrzędnego z nadrzędnego lub z okna przeglądarki, należy skorzystać z metody driver.switchTo().window(). Metoda ta przyjmuje argument w postaci atrybutów name lub handle okna. W poniższym przykładzie użyto atrybutu name.

// Przełączenie się do okna pomocy

driver.switchTo().window("HelpWindow");

Teraz, posługując się egzemplarzem obiektu driver, możemy, tak jak zwykle, wykonywać działania lub weryfikacje wewnątrz okna.

Aby podczas testu przejść do jakiegoś okna z okna nadrzędnego, należy zapisać atrybut handle nadrzędnego okna w zmiennej. Dzięki temu po zakończeniu operacji w oknie podrzędnym można przełączyć się do okna nadrzędnego. Oto przykład zapisania uchwytu.

// Zapisanie uchwytu WindowHandle nadrzędnego okna przeglądarki

String parentWindowId = driver.getWindowHandle();

Test może teraz przełączyć się do okna nadrzędnego za pomocą jego atrybutu handle w następujący sposób.

// Przejście z powrotem do nadrzędnego okna przeglądarki

driver.switchTo().window(parentWindowId);

NoSuchWindowException: Jeśli metoda driver.switchTo().window() nie zdoła zidentyfikować żądanego okna, zgłasza wyjątek NoSuchWindowException.

Co dalej?

Okna można zamykać, wywołując metodę driver.close(). Jednak programiści mogą zaimplementować funkcjonalność zamykania okna w wyniku kliknięcia przycisku albo łącza. W takim przypadku bezpośrednie zamknięcie okna może prowadzić do błędów lub wyjątków.

Zobacz też

Receptura „Identyfikowanie i obsługa okien na podstawie tytułu”,

Receptura „Identyfikowanie i obsługa wyskakujących okien na podstawie zawartości”.

Identyfikowanie i obsługa okien na podstawie tytułu

Wielokrotnie zdarza się, że programiści nie przypisują atrybutu name do okien. W takich przypadkach można użyć jego atrybutu handle. Jednakże atrybuty handle ciągle się zmieniają, przez co identyfikowanie okien staje się trudne, zwłaszcza wtedy gdy jest otwarte więcej niż jedno okno. Wykorzystując atrybuty handle i title strony wyświetlanej w oknie, można zbudować bardziej niezawodny sposób identyfikowania okien podrzędnych.

W tej recepturze użyję atrybutu title do zidentyfikowania okna, a następnie wykonam na nim operacje.

Jak to zrobić?

Utworzymy test, który pobiera uchwyty wszystkich okien otwartych w bieżącym kontekście sterownika. Będziemy iterować po tej liście i sprawdzać, czy tytuł spełnia podane kryteria, w następujący sposób.

@Test

public void testWindowUsingTitle() {

    // Zapamiętanie uchwytu WindowHandle nadrzędnego okna przeglądarki

    String parentWindowId = driver.getWindowHandle();

    // Kliknięcie przycisku Odwiedź nas spowoduje otwarcie strony Odwiedź nas w nowym oknie potomnym

driver.findElement(By.id("visitbutton")).click();

    // Pobranie uchwytów wszystkich otwartych okien,

    // iterowanie po liście i sprawdzanie, czy tytuł

    // każdego okna pasuje do oczekiwanego tytułu okna

    try {

        for (String windowId : driver.getWindowHandles()) {

            String title = driver.switchTo().window(windowId).getTitle();

        if (title.equals("Odwiedź nas")) {

            assertEquals("Odwiedź nas", driver.getTitle());

            // Zamknięcie okna Odwiedź nas

            driver.close();

        break;

        }

        }

    } finally {

        // Przełączenie się do nadrzędnego okna przeglądarki

        driver.switchTo().window(parentWindowId);

    }

    // Sprawdzenie, czy kontekstem sterownika jest nadrzędne okno przeglądarki

    assertEquals("Zbuduj swój samochód - konfiguracja", driver.getTitle());

}

Jak to działa?

Metoda driver.getWindowHandles() zwraca listę zawierającą uchwyty wszystkich otwartych okien. Możemy iterować po tej liście i w celu znalezienia pasujących okien sprawdzać tytuł każdego okna, posługując się atrybutem handle. Można to zrobić w następujący sposób.

for (String windowId : driver.getWindowHandles()) {

        String title =

driver.switchTo().window(windowId).getTitle();

        if (title.equals("Odwiedź nas")) {

            assertEquals("Odwiedź nas", driver.getTitle());

            // Zamknięcie okna Odwiedź nas

            driver.close();

            break;

        }

    }

Do identyfikacji wyskakujących okien za pomocą atrybutu title można utworzyć funkcję wielokrotnego użytku.

Zobacz też

Receptura „Identyfikowanie i obsługa ramek”,

Receptura „Identyfikowanie i obsługa wyskakujących okien na podstawie zawartości”.

Identyfikowanie i obsługa wyskakujących okien na podstawie zawartości

W niektórych sytuacjach programiści nie przypisują atrybutu name ani nie podają tytułu strony wyświetlanej w oknie. Staje się to bardziej złożone, gdy test musi obsługiwać wiele otwartych okien w tym samym czasie, a trzeba zidentyfikować żądane okno.

Obejściem tego problemu może być identyfikowanie żądanego okna w wyniku sprawdzenia zawartości każdego z okien zwróconych przez metodę driver.getWindowHandles().

Jak to zrobić?

Utwórzmy test, który pobiera uchwyty wszystkich okien otwartych w bieżącym kontekście sterownika. Później będzie on iterować po tej liście, przełączać się do poszczególnych okien, a następnie sprawdzać ich zawartość. To pomoże w zidentyfikowaniu odpowiedniego okna, tak jak pokazano w następującym kodzie.

@Test

Public void testWindowUsingContents() {}

    // Zapamiętanie uchwytu WindowHandle nadrzędnego okna przeglądarki

    String currentWindowId = driver.getWindowHandle();

    // Kliknięcie przycisku Rozmowa spowoduje otwarcie strony czatu w nowym oknie potomnym

    driver.findElement(By.id("chatbutton")).click();

    // Dla strony czatu nie wprowadzono nazwy ani tytułu.

    // Aby znaleźć okno czatu, będziemy iterować po wszystkich otwartych oknach

    // i sprawdzać ich zawartość

    try {

        for (String windowId : driver.getWindowHandles()) {

            driver.switchTo().window(windowId);

            // Do sprawdzenia zawartości użyjemy źródła strony

            String pageSource = driver.getPageSource();

            If (pageSource.contains ("Konfiguracja - czat Online")) {

                // Wyszukanie na stronie elementu wyświetlającego oczekiwany

                // komunikat

                assertTrue(driver.findElement(By.tagName("p")).getText()

                    .equals("Czekaj na połączenie z czatem..."));

                // Wyszukanie przycisku Zamknij w oknie czatu i zamknięcie okna

                // poprzez jego kliknięcie

                driver.findElement(By.id("closebutton")).click();

                break;

            }

        }

    } finally {

        // Przełączenie się do nadrzędnego okna przeglądarki

        driver.switchTo().window(currentWindowId);

    }

    // Sprawdzenie, czy kontekstem sterownika jest nadrzędne okno przeglądarki

    assertEquals("Zbuduj swój samochód - konfiguracja", driver.getTitle());

}

Jak to działa?

Wywołując metodę driver.getWindowHandles(), test będzie iterował po otwartych oknach, przełączał się do nich, a następnie sprawdzał, czy w oknie znajduje się żądana zawartość. Do tego celu może służyć następujący fragment kodu.

for (String windowId : driver.getWindowHandles()) {

        driver.switchTo().window(windowId);

        // Do sprawdzenia zawartości będzie wykorzystane źródło strony

        String pageSource = driver.getPageSource();

        If (pageSource.contains ("Konfiguracja - czat Online")) {

            // Wyszukanie na stronie elementu wyświetlającego oczekiwany

            // komunikat

            assertTrue(driver.findElement(By.tagName("p")).getText()

                .equals("Czekaj na połączenie z czatem..."));

        // Wyszukanie przycisku Zamknij w oknie czatu i zamknięcie okna

        // poprzez jego kliknięcie

        driver.findElement(By.id("closebutton")).click();

        break;

    }

}

W tym przykładzie test sprawdza, czy na stronie po wywołaniu metody driver.getPageSource() wyświetla się specyficzny tekst.

Jeśli zostanie znalezione okno zawierające specyficzny tekst, zostanie ono zamknięte poprzez kliknięcie przycisku Zamknij zamiast wywołania metody driver.close(). Taki mechanizm można zaimplementować w teście, gdy nie można zidentyfikować okien na podstawie atrybutów name albo title. Wykorzystanie tego mechanizmu pomaga w budowaniu bardziej wiarygodnych testów.

Zobacz też

Receptura „Identyfikowanie i obsługa ramek”,

Receptura „Identyfikowanie i obsługa okien na podstawie tytułu”.

Rozdział 7.
Testowanie sterowane danymi

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Tworzenie testów sterowanych danymi z wykorzystaniem JUnit.

Tworzenie testów sterowanych danymi z wykorzystaniem TestNG.

Odczyt danych testowych z pliku CSV przy użyciu JUnit.

Odczyt danych testowych z pliku Excela za pomocą JUnit i Apache POI.

Tworzenie testów sterowanych danymi z wykorzystaniem NUnit.

Tworzenie testów sterowanych danymi z wykorzystaniem MSTEST.

Tworzenie testów sterowanych danymi w Ruby z wykorzystaniem Roo.

Tworzenie testów sterowanych danymi w Pythonie z wykorzystaniem DDT.

Wprowadzenie

Testowanie sterowane danymi to metodyka szeroko stosowana w automatyzacji testowania oprogramowania. Po przekazaniu zestawu danych do skryptu testu możemy użyć tego samego skryptu testu do sprawdzania różnych warunków.

Aby zrozumieć zastosowanie techniki testowania sterowanego danymi, skorzystamy z aplikacji kalkulatora BMI.

Podczas testowania, czy aplikacja o nazwie Kalkulator BMI poprawnie wskazuje kategorię BMI, zamiast pisania oddzielnego skryptu dla każdej kategorii możemy wykorzystać jeden skrypt, który będzie wprowadzał wzrost i wagę ze zbioru dostępnych wartości, a następnie sprawdzał wartości oczekiwane.

Do testowania aplikacji kalkulatora BMI możemy użyć następujących kombinacji warunków testowych.

Wzrost [centymetry]

Waga [kilogramy]

BMI

Kategoria

160

45

17.6

Niedowaga

168

70

24.8

Waga normalna

181

89

27.2

Nadwaga

178

100

31.6

Otyłość

Mówiąc najprościej, tester dostarcza z wiersza tabeli dane wejściowe oraz oczekiwane wyniki, które występują w tym samym wierszu.

Testowanie sterowane danymi — przepływ

Przy stosowaniu podejścia testowania sterowanego danymi dane testowe mogą mieć postać tabel w różnych formatach, takich jak pliki CSV, arkusze kalkulacyjne Excela czy bazy danych.

Skrypty testowe implementujemy w następujący sposób: po przeczytaniu wiersz po wierszu danych wejściowych i wynikowych z plików danych przekazujemy wartości do głównego kodu testu. Następnie kod testu nawiguje po aplikacji, wykonując czynności wymagane dla przypadku testowego z wykorzystaniem zmiennych załadowanych za pośrednictwem wartości danych.

Testy bazujące na danych idealnie nadają się do wykorzystania w aplikacjach wymagających obliczeń, do testowania zakresów wartości, wartości granicznych i przypadków brzegowych (ang. corner cases).

Korzyści z testów sterowanych danymi

Testowanie sterowane danymi może przynosić następujące korzyści.

Z pomocą testów sterowanych danymi możemy uzyskać większe pokrycie testu, a jednocześnie zminimalizować ilość kodu testu, którą trzeba napisać, a później utrzymywać.

Testowanie sterowane danymi ułatwia tworzenie i uruchamianie wielu warunków testowych.

Dane testowe mogą być zaprojektowane i utworzone, zanim aplikacja będzie gotowa do testowania.

Tabele danych mogą być również użyte w testowaniu ręcznym.

Selenium WebDriver jest interfejsem API automatyzacji przeglądarek i nie zawiera wbudowanej obsługi testowania sterowanego danymi. Istnieje jednak kilka możliwości dodania obsługi testowania sterowanego danymi do API Selenium WebDriver. W tym rozdziale utworzę kilka podstawowych testów sterowanych danymi z wykorzystaniem JUnit i TestNG. Dalej w tym rozdziale zbuduję kilka zaawansowanych testów bazujących na danych z wykorzystaniem różnych źródeł danych w JUnit. Utworzę także test bazujący na danych w Ruby, za pomocą Roo i Pythona.

Zbuduję też testy sterowane danymi dla platformy .NET, korzystając z pakietów NUnit i MSTest.

Tworzenie testów sterowanych danymi z wykorzystaniem JUnit

JUnit jest popularnym frameworkiem testowania używanym do tworzenia testów Selenium WebDriver w Javie. Testy Selenium WebDriver sterowane danymi można opracować, stosując własności parametryzacji pakietu JUnit 4. Można to zrobić za pomocą klasy uruchamiającej (ang. runner) sparametryzowanych klas JUnit.

W tej recepturze do przetestowania wspomnianej aplikacji kalkulatora BMI utworzę prosty przypadek testowy JUnit. Dane testowe zdefiniuję wewnątrz klasy przypadku testowego JUnit. Do utworzenia testów sterowanych danymi użyję różnych adnotacji JUnit.

Przygotuj się

W ramach przygotowań trzeba wykonać następujące czynności.

Skonfigurować nowy projekt i dodać JUnit4 do ścieżki kompilacji projektu. Projekt można utworzyć w wybranym środowisku IDE.

Zidentyfikować zestaw wartości, które należy przetestować.

Jak to zrobić?

Utwórzmy test sterowany danymi za pomocą JUnit, wykonując następujące czynności.

Utwórz nową klasę testu JUnit, która używa sparametryzowanej klasy uruchamiającej, przy użyciu adnotacji @RunWith(Parameterized.class).

package com.secookbook.examples.chapter07;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.junit.*;

import org.junit.runner.RunWith;

import org.junit.runners.Parameterized.Parameters;

import org.junit.runners.Parameterized;

import static org.junit.Assert.*;

import java.util.Arrays;

import java.util.List;

@RunWith(Parameterized.class)

public class SimpleDDT {

    private static WebDriver driver;

}

Zadeklaruj zmienne reprezentujące wartości parametrów w klasie SimpleDDT.

private String height;

private String weight;

private String bmi;

private String bmiCategory;

Zdefiniuj metodę, która będzie zwracać zbiór parametrów do klasy SimpleDDT z wykorzystaniem adnotacji @Parameters.

@Parameters

public static List<String[]> testData() {

    return Arrays.asList(new String[][] {

        { "160", "45", "17.6", "Niedowaga" },

        { "168", "70", "24.8", "Waga normalna" },

        { "181", "89", "27.2", "Nadwaga" },

        { "178", "100", "31.6", "Otyłość" } });

}

Dodaj do klasy SimpleDDT konstruktor, który będzie używany przez obiekt klasy uruchamiającej testy w celu przekazania parametrów do egzemplarza klasy SimpleDDT.

public SimpleDDT(String height, String weight, String bmi,

String bmiCategory)

{

    this.height = height;

    this.weight = weight;

    this.bmi = bmi;

    this.bmiCategory = bmiCategory;

}

Na koniec dodaj metodę przypadku testowego testBMICalculator() wykorzystującą sparametryzowane zmienne. Dodaj do klasy SimpleDDT metody setup () i teardown().

@Test

public void testBMICalculator() throws Exception {

    // Pobranie elementu opisującego wzrost i ustawienie wartości za pomocą zmiennej height

    WebElement heightField = driver.findElement(By.name("heightCMS"));

    heightField.clear();

    heightField.sendKeys(height);

    // Pobranie elementu opisującego wagę i ustawienie wartości za pomocą zmiennej weight

    WebElement weightField = driver.findElement(By.name("weightKg"));

    weightField.clear();

    weightField.sendKeys(weight);

    // Kliknięcie przycisku Oblicz

    WebElement calculateButton = driver.findElement(By.id("Calculate"));

    calculateButton.click();

    // Pobranie elementu opisującego BMI i sprawdzenie jego wartości przez porównanie ze zmienną bmi

    WebElement bmiLabel = driver.findElement(By.name("bmi"));

    assertEquals(bmi, bmiLabel.getAttribute("value"));

    // Pobranie elementu reprezentującego kategorię BMI i sprawdzenie jego wartości z wykorzystaniem

    // zmiennej bmiCategory

    WebElement bmiCategoryLabel = driver.findElement(By.name("bmi_category"));

    assertEquals(bmiCategory,

bmiCategoryLabel.getAttribute("value"));

}

Jak to działa?

Test jest uruchamiany dla każdego wiersza w kolekcji danych testowych. Obiekt klasy uruchamiającej tworzy egzemplarz klasy przypadku testowego, przekazując do konstruktora klasy SimpleDDT parametry reprezentujące dane testowe. Następnie uruchamia wszystkie testy wewnątrz klasy SimpleDDT.

Zadeklarowane zmienne egzemplarza służą do przechowywania danych przekazywanych przez obiekt uruchamiający testy, tak jak pokazano w poniższym fragmencie kodu.

private String height;

private String weight;

private String bmi;

private String bmiCategory;

W czasie działania aplikacji w konstruktorze klasy przypadku testowego do tych zmiennych są przypisywane wartości pobierane przez obiekt uruchamiający z kolekcji danych testowych. Do tego celu wykorzystywane są argumenty metod testowych, tak jak pokazano w poniższym kodzie.

public SimpleDDT(String height, String weight, String bmi, String

bmiCategory)

{

    this.height = height;

    this.weight = weight;

    this.bmi = bmi;

    this.bmiCategory = bmiCategory;

}

W metodzie testBMICalculator() zmienne te przekazaliśmy do API Selenium WebDriver. Aby na przykład wprowadzić wartość w polu opisującym wzrost, użyliśmy zmiennej egzemplarza klasy reprezentującej przypadek testowy w następujący sposób.

WebElement heightField = driver.findElement(By.name("heightCMS"));

heightField.sendKeys(this.height);

Oczekiwany wynik jest również sparametryzowany przy użyciu zmiennej egzemplarza reprezentującej wartość współczynnika BMI, tak jak pokazano w poniższym kodzie.

WebElement bmiLabel = driver.findElement(By.name("bmi"));

assertEquals(this.bmi, bmiLabel.getAttribute("value"));

JUnit wyświetli wyniki dla każdego zestawu danych testowych wraz z czasem potrzebnym do jego uruchomienia, tak jak pokazano na poniższym zrzucie ekranu.

Zaprezentowany sposób jest jedną z najprostszych metod parametryzacji testów. Dane testowe są jednak zakodowane „na sztywno” w klasie reprezentującej przypadek testowy, co może stwarzać trudności w utrzymaniu testów. W celu ułatwienia utrzymania zaleca się przechowywanie danych testowych w zewnętrznym źródle danych, na przykład pliku CSV, arkuszu Excela lub pliku bazy danych.

Zobacz też

Receptura „Odczytywanie danych testowych z pliku CSV z wykorzystaniem JUnit”,

Receptura „Odczytywanie danych testowych z pliku Excela z wykorzystaniem JUnit i Apache POI”.

Tworzenie testów sterowanych danymi z wykorzystaniem frameworka TestNG

TestNG to inny framework testowania powszechnie używany razem z Selenium WebDriver. Jest bardzo podobny do JUnit. TestNG ma rozbudowane funkcje testowania, czyli obsługę parametryzacji, równoległe wykonywanie testów i tak dalej.

TestNG dostarcza funkcję DataProvider, która pozwala tworzyć testy sterowane danymi. W tej recepturze wykorzystam klasę DataProvider do utworzenia prostego testu. Tworzenie testów sterowanych danymi w TestNG jest dość łatwe w porównaniu z taką samą procedurą w JUnit.

Przygotuj się

Poniżej zamieszczono czynności, od których należy zacząć.

Dodaj zależność od biblioteki TestNG w pliku Maven pom.xml.

<dependency>

    <groupId>org.testng</groupId>

    <artifactId>testng</artifactId>

    <version>6.9.4</version>

    <scope>test</scope>

</dependency>

Zidentyfikuj zbiór wartości, które należy przetestować.

Jak to zrobić?

Utwórz nowy test TestNG i sparametryzuj go, wykorzystując następujący kod.

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.testng.annotations.*;

import static org.testng.Assert.*;

public class TestNGDDT {

    private WebDriver driver;

    @DataProvider

    public Object[][] testData() {

        return new Object[][] {

            new Object[] { "160", "45", "17.6", "Niedowaga"},

            new Object[] { "168", "70", "24.8", "Waga normalna" },

            new Object[] { "181", "89", "27.2", "Nadwaga" },

            new Object[] { "178", "100", "31.6", "Otyłość" }, };

    }

    @BeforeTest

    public void setUp() {

        // Utworzenie nowego egzemplarza sterownika przeglądarki Firefox

        driver = new FirefoxDriver();

        driver.get("http://cookbook.seleniumacademy.com/bmicalculator.html");

    }

    @Test(dataProvider = "testData")

    public void testBMICalculator(String height, String weight, String bmi,

            String category) {

        WebElement heightField = driver.findElement(By.name("heightCMS"));

        heightField.clear();

        heightField.sendKeys(height);

        WebElement weightField = driver.findElement(By.name("weightKg"));

        weightField.clear();

        weightField.sendKeys(weight);

        WebElement calculateButton = driver.findElement(By.id("Calculate"));

        calculateButton.click();

        WebElement bmiLabel = driver.findElement(By.name("bmi"));

        assertEquals(bmiLabel.getAttribute("value"), bmi);

        WebElement bmiCategoryLabel = driver.findElement(By

            .name("bmi_category"));

        assertEquals(bmiCategoryLabel.getAttribute("value"), category);

    }

    @AfterTest

    public void tearDown() {

        driver.quit();

    }

}

Jak to działa?

W odróżnieniu od frameworka JUnit, gdzie parametryzacja odbywa się na poziomie klasy, TestNG obsługuje parametryzację na poziomie testu.

W TestNG nie potrzebujemy konstruktora i zmiennej egzemplarza klasy reprezentującej przypadek testowy w celu przekazania wartości parametrów. TestNG realizuje mapowanie automatycznie.

Gdy do metody zostanie dodana adnotacja @DataProvider, to przez przekazanie danych testowych do przypadku testowego staje się ona metodą dostarczającą dane. W tym przykładzie metodą dostarczającą dane stanie się testData(), a framework TestNG będzie po kolei przekazywał do metody testu tablicę zawierającą wiersze danych.

@DataProvider

public Object[][] testData() {

    return new Object[][] {

        new Object[] {"160","45","17.6","Niedowaga"},

        new Object[] {"168","70","24.8","Waga normalna"},

        new Object[] {"181","89","27.2","Nadwaga"},

        new Object[] {"178","100","31.6","Otyłość"},

    };

}

Metoda przypadku testowego jest powiązana z metodą dostarczającą dane za pomocą przekazania nazwy metody dostawcy danych do adnotacji @Test.

@Test(dataProvider = "testData")

public void testBMICalculator(String height, String weight, String

bmi, String category)

TestNG wykona test cztery razy, wykorzystując różne kombinacje danych. Na końcu działania testu TestNG wygeneruje również estetycznie sformatowany raport.

Co dalej?

Własność parametryzacji dostarczana przez framework TestNG bardzo się przydaje do równoległego uruchamiania testów Selenium. TestNG obsługuje równoległe uruchamianie testów w bezpiecznym, wielowątkowym środowisku.

Zobacz też

Receptura „Odczytywanie danych testowych z pliku CSV z wykorzystaniem JUnit”,

Receptura „Odczytywanie danych testowych z pliku Excela z wykorzystaniem JUnit i Apache POI”.

Odczytywanie danych testowych z pliku CSV z wykorzystaniem JUnit

Powyżej pokazałem prosty test sterowany danymi, korzystający z frameworków JUnit i TestNG. Dane testowe zostały zakodowane „na sztywno” w kodzie skryptu testu. Taki sposób może spowodować problemy z utrzymaniem. Zaleca się, aby dane były przechowywane oddzielnie, nie ze skryptami testowymi.

Często do testowania używa się danych z środowiska produkcyjnego. Te dane mogą być wyeksportowane w formacie CSV. Do odczytu pliku CSV użyję biblioteki OpenCSV. Więcej informacji na temat OpenCSV można znaleźć pod adresem http://opencsv.sourceforge.net/.

W tej recepturze odczytam dane z pliku CSV i użyję ich do uruchomienia skryptu testowego.

Przygotuj się

Na początek wykonaj następujące kroki.

Do pliku Maven pom.xml dodaj zależność od biblioteki OpenCSV.

<dependency>

    <groupId>com.opencsv</groupId>

    <artifactId>opencsv</artifactId>

    <version>3.4</version>

    <scope>test</scope>

</dependency>

Przygotuj plik CSV z potrzebnymi danymi.

Jak to zrobić?

Utwórzmy nowy test JUnit i plik parametrów CSV, tak jak pokazano w poniższym przykładzie.

package com.secookbook.examples.chapter07;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.junit.*;

import org.junit.runner.*;

import org.junit.runners.*;

import org.junit.runners.Parameterized.Parameters;

import com.opencsv.CSVReader;

import static org.junit.Assert.*;

import java.io.*;

import java.util.*;

@RunWith(Parameterized.class)

public class CsvTestData {

    private static WebDriver driver;

    private String height;

    private String weight;

    private String bmi;

    private String bmiCategory;

    @Parameters

    public static List<String[]> testData() throws IOException {

        return getTestData("./src/test/resources/testdata/data.csv");

    }

    public CsvTestData(String height, String weight, String bmi,

            String bmiCategory) {

        this.height = height;

        this.weight = weight;

        this.bmi = bmi;

        this.bmiCategory = bmiCategory;

    }

    public static List<String[]> getTestData(String fileName)

            throws IOException {

        CSVReader reader = new CSVReader(new

FileReader(fileName));

        List<String[]> myEntries = reader.readAll();

        reader.close();

        return myEntries;

    }

    @BeforeClass

    public static void setUp() throws Exception {

        // Utworzenie nowego egzemplarza sterownika przeglądarki Firefox

        driver = new FirefoxDriver();

        driver.get("http://cookbook.seleniumacademy.com/bmicalculator.html");

    }

    @Test

    public void testBMICalculator() throws Exception {

        WebElement heightField =

driver.findElement(By.name("heightCMS"));

        heightField.clear();

        heightField.sendKeys(height);

        WebElement weightField =

driver.findElement(By.name("weightKg"));

        weightField.clear();

        weightField.sendKeys(weight);

         WebElement calculateButton =

driver.findElement(By.id("Calculate"));

        calculateButton.click();

        WebElement bmiLabel =

driver.findElement(By.name("bmi"));

        assertEquals(bmi, bmiLabel.getAttribute("value"));

        WebElement bmiCategoryLabel = driver.findElement(By

            .name("bmi_category"));

        assertEquals(bmiCategory,

bmiCategoryLabel.getAttribute("value"));

    }

    @AfterClass

    public static void tearDown() throws Exception {

        // Zamknięcie przeglądarki

        driver.quit();

    }

}

Jak to działa?

Po uruchomieniu testu metoda testData() wywoła metodę pomocniczą getTestData() i przekaże ścieżkę do pliku CSV. Wewnątrz metody getTestData() do czytania pliku wiersz po wierszu używana jest klasa BufferedReader z przestrzeni nazw java.io. Odczytane wiersze są następnie dzielone na tablicę ciągów znaków za pomocą separatora — przecinka. Tablica jest dodawana do egzemplarzy Collection lub ArrayList, a egzemplarz zwracany do metody testData().

Dla każdego wiersza w kolekcji danych testowych zwróconych przez metodę testData() obiekt uruchamiający test utworzy egzemplarz obiektu reprezentującego przypadek testowy, przekazując dane testowe jako parametry do konstruktora klasy testu. Następnie uruchomi wszystkie testy wewnątrz klasy test.

Zobacz też

Receptura „Tworzenie testów sterowanych danymi z wykorzystaniem JUnit”,

Receptura „Odczytywanie danych testowych z pliku Excela z wykorzystaniem JUnit i Apache POI”.

Odczytywanie danych testowych z pliku Excela z wykorzystaniem JUnit i Apache POI

Microsoft Excel jest ulubionym narzędziem wykorzystywanym przez testerów do utrzymywania przypadków i danych testowych. Excel, w porównaniu z formatem CSV, oferuje liczne funkcje oraz strukturalny sposób przechowywania danych. Tester może łatwo tworzyć tabele danych testowych w arkuszu kalkulacyjnym Excela i zarządzać nimi.

W tej recepturze użyję arkusza kalkulacyjnego Excela w roli źródła danych. W celu manipulowania arkuszem kalkulacyjnym Excela skorzystam z API Apache POI opracowanego przez Apache Foundation. W tej recepturze zaimplementowałem również obsługę kilku testów negatywnych.

Przygotuj się

Na początek wykonaj następujące czynności.

Do pliku Maven pom.xml dodaj zależność od biblioteki OpenCSV.

<dependency>

    <groupId>com.opencsv</groupId>

    <artifactId>opencsv</artifactId>

    <version>3.4</version>

    <scope>test</scope>

</dependency>

Przygotuj arkusz kalkulacyjny Excela z potrzebnymi danymi.

Do czytania arkuszy kalkulacyjnych Excela potrzebna będzie również klasa pomocnicza SpreadsheetData. Jest ona dostępna w pakiecie z kodem źródłowym dołączonym do tej książki. Klasa obsługuje zarówno stary format .xls, jak i nowszy format .xlsx.

Jak to zrobić?

Utwórzmy test, który do parametryzacji używa danych testowych Excela. Jego kod zaprezentowano w poniższym kodzie.

package com.secookbook.examples.chapter07;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.junit.*;

import org.junit.runner.RunWith;

import org.junit.runners.Parameterized.Parameters;

import org.junit.runners.Parameterized;

import static org.junit.Assert.*;

import java.io.FileInputStream;

import java.io.InputStream;

import java.util.Collection;

@RunWith(Parameterized.class)

public class ExcelTestData {

    private static WebDriver driver;

    private String height;

    private String weight;

    private String bmi;

    private String bmiCategory;

    private String error;

    @Parameters

    public static Collection testData() throws Exception {

        InputStream spreadsheet = new FileInputStream("./src/test/resources/testdata/Data.xlsx");

        return new SpreadsheetData(spreadsheet).getData();

    }

    public ExcelTestData(String height, String weight, String bmi,

            String bmiCategory, String error) {

        this.height = height;

        this.weight = weight;

        this.bmi = bmi;

        this.bmiCategory = bmiCategory;

        this.error = error;

    }

    @BeforeClass

    public static void setUp() throws Exception {

        // Utworzenie nowego egzemplarza sterownika przeglądarki Chrome

        driver = new ChromeDriver();

        driver.get("http://cookbook.seleniumacademy.com/bmicalculator.html");

    }

    @Test

    public void testBMICalculator() throws Exception {

        WebElement heightField = driver.findElement(By.name("heightCMS"));

        heightField.clear();

        if (!height.equals("<Blank>")) {

            heightField.sendKeys(this.height);

        }

        WebElement weightField =

driver.findElement(By.name("weightKg"));

        weightField.clear();

        if (!weight.equals("<Blank>")) {

            weightField.sendKeys(this.weight);

        }

        WebElement calculateButton =

driver.findElement(By.id("Calculate"));

        calculateButton.click();

        if (error.equals("<Blank>")) {

            WebElement bmiField = driver.findElement(By.name("bmi"));

            assertEquals(this.bmi, bmiField.getAttribute("value"));

            WebElement bmiCategoryField = driver.findElement(By

                .name("bmi_category"));

            assertEquals(this.bmiCategory,

                bmiCategoryField.getAttribute("value"));

        } else {

            WebElement errorLabel =

driver.findElement(By.id("error"));

            assertEquals(this.error, errorLabel.getText());

        }

    }

    @AfterClass

    public static void tearDown() throws Exception {

        driver.quit();

    }

}

Jak to działa?

Po uruchomieniu testu metoda testData() tworzy egzemplarz klasy SpreadsheetData. Klasa SpreadsheetData odczytuje zawartość arkusza Excela wiersz po wierszu do kolekcji i zwraca tę kolekcję do metody testData().

InputStream spreadsheet = new

FileInputStream("./src/test/resources/testdata/Data.xlsx");

return new SpreadsheetData(spreadsheet).getData();

Dla każdego wiersza w kolekcji danych testowych zwracanych przez metodę testData() klasa uruchamiająca test utworzy egzemplarz klasy testowej, przekazując do konstruktora klasy testu dane testowe jako parametry, a następnie uruchomi wszystkie testy w klasie testowej.

Zobacz też

Receptura „Tworzenie testów sterowanych danymi z wykorzystaniem JUnit”,

Receptura „Odczytywanie danych testowych z pliku CSV z wykorzystaniem JUnit”.

Tworzenie testów sterowanych danymi z wykorzystaniem NUnit

Framework NUnit jest szeroko stosowany przez społeczność Selenium WebDriver do tworzenia skryptów testowych zawierających dowiązania do platformy .NET.

Framework NUnit, podobnie jak JUnit, obsługuje proste testy sterowane danymi. W tej recepturze utworzę test Selenium WebDriver, korzystając z frameworka NUnit. Dane testowe zostaną odczytane z pliku XML używanego w pierwszej recepturze.

Przygotuj się

Na początek wykonaj następujące czynności.

Pobierz NUnit spod adresu http://www.nunit.org/ i zainstaluj.

Utwórz plik danych testowych w formacie XML w następujący sposób.

    <testdata>

    <vars height="160" weight="45" bmi="17.6"

bmi_category="Niedowaga" />

    <vars height="168" weight="70" bmi="24.8"

bmi_category="Waga normalna" />

    <vars height="181" weight="89" bmi="27.2"

bmi_category="Nadwaga" />

    <vars height="178" weight="100" bmi="31.6"

bmi_category="Otyłość" />

</testdata>

Utwórz nowy projekt C# typu „biblioteka klas” i nadaj mu nazwę BMICalculator.

Dodaj referencje do NUnit, WebDriver, biblioteki dowiązań .NET, System.XML i System.Xml.Linq.

Jak to zrobić?

Posługując się frameworkiem NUnit, zbudujmy sparametryzowany test. Utwórz nową klasę C# o nazwie BMICalculatorNUnitTest, tak jak pokazano w przykładowym kodzie. Skopiuj do klasy poniższy kod.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using NUnit.Framework;

using System.Collections;

using System.Xml.Linq;

using OpenQA.Selenium;

using OpenQA.Selenium.Firefox;

using OpenQA.Selenium.Support;

using OpenQA.Selenium.Support.UI;

namespace BMICalculator

{

    [TestFixture]

    public class BMICalculatorNUnitTest

    {

        IWebDriver driver;

        [SetUp]

        public void TestSetup()

        {

            // Utworzenie egzemplarza sterownika przeglądarki Firefox za pomocą interfejsu IWebDriver

            driver = new FirefoxDriver();

        }

        [TestCaseSource("BmiTestData")]

        public void TestBmiCalculator(string height, string

weight, string expectedBmi, string expectedCategory)

        {

            driver.Navigate().GoToUrl("http://cookbook.

seleniumacademy.com/bmicalculator.html");

            IWebElement heightElement =

driver.FindElement(By.Name("heightCMS"));

            heightElement.SendKeys(height);

            IWebElement weightElement =

driver.FindElement(By.Name("weightKg"));

            weightElement.SendKeys(weight);

            IWebElement calculateButton =

driver.FindElement(By.Id("Calculate"));

            calculateButton.Click();

            IWebElement bmiElement =

driver.FindElement(By.Name("bmi"));

            Assert.AreEqual(expectedBmi,

bmiElement.GetAttribute("value"));

            IWebElement bmiCatElement =

driver.FindElement(By.Name("bmi_category"));

            Assert.AreEqual(expectedCategory,

bmiCatElement.GetAttribute("value"));

        }

        [TearDown]

        public void TestCleanUp()

        {

            // Zamknięcie przeglądarki

            driver.Quit();

        }

        private IEnumerable BmiTestData

        {

            get { return GetBmiTestData(); }

        }

        private IEnumerable GetBmiTestData()

        {

            var doc = XDocument.Load(@"c:\data.xml");

            return

                from vars in doc.Descendants("vars")

                let height = vars.Attribute("height").Value

                let weight = vars.Attribute("weight").Value

                let expectedBmi =

vars.Attribute("bmi").Value

                let expectedCategory =

vars.Attribute("bmi_category").Value

                select new object[] { height, weight,

expectedBmi, expectedCategory };

        }

    }

}

Jak to działa?

Podczas tworzenia testów sterowanych danymi w NUnit możemy użyć atrybutu TestCaseSource. Określamy nazwę właściwości IEnumerable, która dostarcza dane testowe do tego przypadku testowego z użyciem atrybutu TestCaseSource, tak jak pokazano w poniższym kodzie.

[TestCaseSource("BmiTestData")]

public void TestBmiCalculator(string height, string weight, string

expected_bmi, string expected_category)

Kiedy wykonujemy test, NUnit framework generuje przypadki testowe przez wywoływanie właściwości BmiTestData. Wywołanie metody GetBmiTestData() powoduje zwrócenie tablicy argumentów jako IEnumerable. Tablica argumentów jest tworzona w wyniku odczytu pliku XML za pomocą kwerendy LINQ w metodzie GetBmiTestData(). Kiedy otworzymy test w środowisku GUI frameworka NUnit, wyświetla on przypadki testowe dla wszystkich kombinacji danych testowych dostarczonych w wejściowym pliku XML, tak jak pokazano na pierwszym zrzucie ekranu poniżej.

Dane testowe mogą być odczytywane z dowolnych źródeł, takich jak plik CSV, arkusz kalkulacyjny Excela lub baza danych. Po wykonaniu wszystkich testów w GUI frameworka NUnit wyświetlają się wyniki, tak jak pokazano na drugim zrzucie ekranu poniżej.

Zobacz też

Receptura „Tworzenie testów sterowanych danymi z wykorzystaniem MSTEST”.

Tworzenie testów sterowanych danymi z wykorzystaniem MSTEST

W przypadku tworzenia testów sterowanych danymi z wykorzystaniem MSTEST najprostszym sposobem parametryzacji skryptów testowych z dowiązaniami .NET jest skorzystanie z frameworka testów jednostkowych dostarczanego razem z środowiskiem Microsoft Visual Studio.

MSTEST ma wbudowane funkcje obsługi testów sterowanych danymi, które można bardzo łatwo skonﬁgurować. W tej recepturze wykorzystam MSTEST do utworzenia testów Selenium sterowanych danymi odczytywanymi z arkusza Excela.

Przygotuj się

Na początek wykonaj następujące czynności.

W środowisku Microsoft Visual Studio 2010 utwórz nowy projekt testu C# i nadaj mu nazwę BMICalculator.

Dodaj referencję do dowiązań .NET dla biblioteki WebDriver.

Jak to zrobić?

Test w MSTEST można sparametryzować, dodając arkusz Excela do elementów projektu testowego. W tym celu należy wykonać następujące czynności.

Kliknij Local.testsettings w obszarze Solution Items.

Wyświetli się okno dialogowe Test Settings. Należy w nim dodać plik danych testowych. W tym celu kliknij przycisk Add File w sekcji Deployment.

Plik po dodaniu w sekcji Deployment wyświetli się na liście.

Utwórz nową klasę testową i nadaj jej nazwę BMICalculatorTests. Skopiuj do tej klasy następujący kod.

using System;

using System.Text;

using System.Collections.Generic;

using System.Linq;

using Microsoft.VisualStudio.TestTools.UnitTesting;

using OpenQA.Selenium;

using OpenQA.Selenium.Firefox;

using OpenQA.Selenium.Support;

using OpenQA.Selenium.Support.UI;

using System.Data;

namespace BMICalculator

{

    [TestClass]

    public class BMICalculatorTests

    {

        IWebDriver driver;

        [TestInitialize]

        public void TestSetup()

        {

            // Utworzenie egzemplarza sterownika przeglądarki Firefox

using IWebDriver Interface

            driver = new FirefoxDriver();

        }

        private TestContext testContextInstance;

        /// <summary>

        /// Pobiera lub ustawia kontekst testowy, który dostarcza

        /// informacje i funkcjonalności do uruchomienia bieżącego testu

        /// </summary>

        public TestContext TestContext

        {

            get

            {

                return testContextInstance;

            }

            set

            {

                testContextInstance = value;

            }

        }

        [TestMethod]

        [DeploymentItem("Data.xls")]

        [DataSource("System.Data.OleDb",

"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Data.xls;Persist

Security Info=False;Extended

Properties='Excel 12.0;HDR=Yes'", "Data$",

DataAccessMethod.Sequential)]

        public void TestBMICalculator()

        {

            driver.Navigate().GoToUrl("http://dl.dropbox.com/u/55228056/bmicalculator.html");

            IWebElement height =

driver.FindElement(By.Name("heightCMS"));

            height.SendKeys(TestContext.DataRow["Height"].

ToString());

            IWebElement weight =

driver.FindElement(By.Name("weightKg"));

            weight.SendKeys(TestContext.DataRow["Weight"].

ToString());

            IWebElement calculateButton =

driver.FindElement(By.Id("Calculate"));

            calculateButton.Click();

            IWebElement bmi =

driver.FindElement(By.Name("bmi"));

            Assert.AreEqual(TestContext.DataRow["Bmi"].ToString(),

bmi.GetAttribute("value"));

            IWebElement bmiCategory =

driver.FindElement(By.Name("bmi_category"));

            Assert.AreEqual(TestContext.DataRow["Category"].

ToString(),

bmiCategory.GetAttribute("value"));

            }

            [TestCleanup]

            public void TestCleanUp()

            {

                // Zamknięcie przeglądarki

                driver.Quit();

        }

    }

}

Jak to działa?

Dodanie do testu w MSTEST atrybutu DataSource dostarcza do frameworka informacje specyficzne dla źródła danych.

[TestMethod]

[DeploymentItem("Data.xls")]

[DataSource("System.Data.OleDb",

"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=Data.xls;Persist

Security Info=False;Extended Properties='Excel 12.0;HDR=Yes'",

"Data$", DataAccessMethod.Sequential)]

public void TestBMICalculator()

Dzięki temu dane testowe są odczytywane ze wskazanego źródła. W tym przykładzie źródłem danych jest arkusz Excela. W celu przechowywania wartości ze źródła danych framework wewnętrznie tworzy obiekt DataTable. Metoda TestContext dostarcza kolekcję wierszy danych do parametryzacji. Dostęp do tego pola można uzyskać poprzez określenie jego nazwy w następujący sposób.

            IWebElement height = driver.FindElement(By.Name("heightCMS"));

            height.SendKeys(TestContext.DataRow["Height"].ToString());

Za pomocą atrybutu DataSource możemy określić ciąg połączenia lub plik konfiguracyjny do odczytu danych z różnych źródeł, w tym z pliku CSV, arkuszy Excela, plików XML lub baz danych.

Zobacz też

Receptura „Tworzenie testów sterowanych danymi z wykorzystaniem NUnit”.

Tworzenie testów sterowanych danymi w Ruby z wykorzystaniem Roo

W poprzednich recepturach zaprezentowałem parametryzację testów w środowiskach Javy i .NET. Do tworzenia testów Selenium WebDriver powszechnie używa się również języka Ruby.

W języku Ruby nie ma wbudowanych mechanizmów parametryzacji skryptów testowych. W Ruby można jednak użyć gemu Roo (http://roo.rubyforge.org/) i za jego pomocą czytać dane z arkuszy kalkulacyjnych. Roo obsługuje wiele formatów. Oto one.

Przechowywany lokalnie plik Excela (.xls).

Przechowywany lokalnie plik OpenOffice (.ods).

Plik Excela (.xls) przechowywany na stronie wiki Confluence dostępny za pośrednictwem pakietu Confluence Office Connector.

Arkusz kalkulacyjny Google Docs.

Roo jest doskonałą alternatywą dla Ruby Excel COM WIN32 API, ponieważ nie wymaga zainstalowania na komputerze klienckim Excela ani OpenOffice. Oba formaty plików są odczytywane natywnie.

W tej recepturze sparametryzuję test Selenium WebDriver utworzony z wykorzystaniem dowiązań Ruby oraz arkuszem kalkulacyjnym Excela jako źródłem danych testowych.

Przygotuj się

Zainstaluj gem Roo, używając następującego polecenia.

gem install roo

gem install roo-xls

To polecenie pobiera i instaluje wszystkie zależności wymagane do działania gemu Roo.

Jak to zrobić?

Utwórzmy prosty test Ruby zaplanowany do sparametryzowania, wykonując czynności opisane poniżej. Ten test będzie czytać dane testowe z arkusza Excela używanego we wcześniejszej recepturze „Odczytywanie danych testowych z pliku Excela z wykorzystaniem JUnit i Apache POI”. Utwórz test Ruby. Zacznij od zaimportowania następujących modułów.

require 'rubygems'

require 'selenium-webdriver'

require 'roo'

require 'roo-xls'

Teraz wykonaj poniższe czynności.

Utwórz egzemplarz klasy WebDriver. Będziemy używać przeglądarki Firefox. Sposób realizacji tego zadania zaprezentowano w poniższym kodzie.

# Utworzenie egzemplarza klasy WebDriver dla programu Firefox

driver = Selenium::WebDriver.for :firefox

Aby wyświetlić zestawienie kombinacji testów uruchamianych ze źródła danych testowych, zadeklaruj następujące zmienne.

# Zmienne do wyświetlania zestawienia testów

test_executed = 0

test_passed = 0

test_failed = 0

Utwórz egzemplarz klasy Excel z gemu Roo odpowiedzialny za odczyt danych z arkusza kalkulacyjnego.

# Utworzenie egzemplarza obiektu reprezentującego arkusz Excela

data = Roo::Excel.new("C:\\Data.xls")

data.default_sheet = data.sheets.first

Dodaj poniższy kod. Jego zadanie polega na iterowaniu po arkuszu kalkulacyjnym, odczytywaniu kombinacji danych testowych, a następnie wykonywaniu operacji oraz ich weryfikowaniu.

# Iterowanie po arkuszu i czytanie danych arkusza wiersz po wierszu

data.first_row.upto(data.last_row) do |line|

    if data.cell(line,1) != "Height" # Ignorujemy pierwszy wiersz zawierający nagłówki

        begin

            test_status = true

            test_executed = test_executed + 1

            puts "Test " + test_executed.to_s()

            driver.get "http://dl.dropbox.com/u/55228056/bmicalculator.html"

            height = driver.find_element :name => "heightCMS"

            height.send_keys data.cell(line,1).to_s()

            weight = driver.find_element :name => "weightKg"

            weight.send_keys data.cell(line,2).to_s()

            calculateButton = driver.find_element :id=>"Calculate"

            calculateButton.click

            bmi = driver.find_element :name =>"bmi"

            bmi_category = driver.find_element :name=>"bmi_category"

            if bmi.attribute("value").to_s() == data.cell(line,3).to_s()

               puts "Wynik pozytywny, oczekiwana wartość BMI <" + data.cell(line,3).

                to_s() + ">, faktyczna <" +

bmi.attribute("value").to_s() + ">"

            else

                puts "Wynik negatywny, oczekiwana wartość BMI <" +

data.cell(line,3).

                to_s() + ">, faktyczna <" +

bmi.attribute("value").to_s() + ">"

                test_status=false

            end

            if bmi_category.attribute("value").to_s() == data.

            cell(line,4).to_s()

                puts "Wynik pozytywny, oczekiwana wartość dla kategorii BMI <" +

                data.cell(line,4).to_s() + ">, faktyczna <" +

bmi_category. attribute("value").to_s() + ">"

            else

                puts "Wynik negatywny, oczekiwana wartość dla kategorii BMI <" +

                data.cell(line,4).to_s() + ">, faktyczna <" +

bmi_category. attribute("value").to_s() + ">"

                test_status=false

            end

            if test_status

                test_passed = test_passed + 1

            else

                test_failed = test_failed + 1

            end

        rescue

            puts "Wystąpił błąd: #{$!}"

        end

    end

end

Na koniec wyświetlamy podsumowanie kombinacji testów, które zostały wykonane i przeszły (testy z wynikiem pozytywnym — ang. passed), oraz tych, które zakończyły się niepowodzeniem (testy z wynikiem negatywnym — ang. failed), przy użyciu następującego kodu.

puts "--------------------------------------------"

puts "Razem (" + test_executed.to_s() + ") testów uruchomionych"

puts "Razem (" + test_passed.to_s() + ") testów zakończonych wynikiem pozytywnym"

puts "Razem (" + test_failed.to_s() + ") testów zakończonych wynikiem negatywnym"

driver.quit

Jak to działa?

Gdy uruchomimy ten test, Roo odczyta zawartość arkusza Excela do obiektu danych, przy użyciu następującego kodu.

# Utworzenie egzemplarza obiektu reprezentującego arkusz Excela

data = Roo::Excel.new("C:\\Data.xls")

data.default_sheet = data.sheets.first

Następnie możemy iterować po obiekcie data od pierwszego do ostatniego wiersza przy użyciu następującego kodu.

data.first_row.upto(data.last_row) do |line|

Spowoduje to skopiowanie zawartości z wiersza do zmiennej o nazwie line. Dostęp do wartości z komórki danych można uzyskać za pomocą metody data.cell(). Należy do niej przekazać wiersz i położenie komórki. Do tego celu można skorzystać z następującego kodu.

height = driver.find_element :name => "heightCMS"

height.send_keys data.cell(line,1).to_s()

Dodaliśmy również niestandardowy kod raportu, który na koniec działania testu wygeneruje estetycznie sformatowany raport podobny do tego, który zaprezentowano na poniższym zrzucie ekranu.

Co dalej?

Gem Roo pozwala również odczytywać arkusze kalkulacyjne Google Docs. Można to zrobić przy użyciu metody Google.new(), przekazując do niej klucz dla arkusza kalkulacyjnego Google Docs, tak jak pokazano w poniższym poleceniu.

data = Google.new("0Al-3LZqhACsidFh1NmdnYktmTkREVEkzb3B0ZnYybHc")

Aby używać arkuszy kalkulacyjnych Google, trzeba albo ustawić zmienne środowiskowe GOOGLE_MAIL i GOOGLE_PASSWORD, albo przekazać nazwę użytkownika i hasło Google do metody Google#new.

Wiele zespołów utrzymuje arkusze kalkulacyjne Excela na stronie Wiki Confluence. Posługując się gemem Roo, można również czytać arkusze kalkulacyjne zapisane na stronach Wiki Confluence. Więcej informacji uzyskamy na macierzystej stronie gemu Roo, pod adresem http://roo.rubyforge.org/.

Tworzenie testów sterowanych danymi w Pythonie z wykorzystaniem DDT

Do tworzenia testów Selenium WebDriver powszechnie używa się również języka Python. Język ten oferuje różne możliwości parametryzowania testów.

W tej recepturze do utworzenia sparametryzowanego testu użyję modułów DDT i unittest.

Przygotuj się

Zainstaluj moduł DDT przy użyciu następującego polecenia.

pip install ddt

Polecenie to pobierze i zainstaluje wszystkie zależności wymagane do korzystania z modułu DDT na Twoim komputerze.

Jak to zrobić?

Utwórzmy w Pythonie prosty test przeznaczony do sparametryzowania przy użyciu modułu DDT. Otwórz nowy plik Pythona o nazwie bmi_calc_ddt.py, używając następującego kodu.

import unittest

from ddt import ddt, data, unpack

from selenium import webdriver

@ddt

class BmiCalcDDT(unittest.TestCase):

    def setUp(self):

        # Utworzenie nowej sesji Firefox

        self.driver = webdriver.Firefox()

        self.driver.implicitly_wait(30)

        self.driver.maximize_window()

        # Przejście do strony kalkulatora BMI

        self.driver.get("http://bit.ly/1zdNrFZ")

    # Określenie danych testowych z wykorzystaniem dekoratora @data

    @data(("160", "45", "17.6", "Niedowaga"),

         ("168", "70", "24.8", "Waga normalna"),

         ("181", "89", "27.2", "Nadwaga"))

    @unpack

    def test_bmi_calc(self, height, weight, bmi, category):

        driver = self.driver

        height_field = driver.find_element_by_name("heightCMS")

        height_field.clear()

        height_field.send_keys(height)

        weight_field = driver.find_element_by_name("weightKg")

        weight_field.clear()

        weight_field.send_keys(weight)

        calculate_button = driver.find_element_by_id("Calculate")

        calculate_button.click()

        bmi_label = driver.find_element_by_name("bmi")

        bmi_category_label = driver.find_element_by_name("bmi_category")

        self.assertEqual(bmi, bmi_label.get_attribute("value"))

        self.assertEqual(category, bmi_category_label.get_attribute("value"))

    def tearDown(self):

        # Zamknięcie okna przeglądarki

        self.driver.quit()

if __name__ == '__main__':

    unittest.main(verbosity=2)

Jak to działa?

Moduł DDT jest rozszerzeniem dla biblioteki Pythona unittest. Moduł ten możemy wykorzystać do sparametryzowania testów jednostkowych w Pythonie. W celu parametryzacji klasy testu należy dodać do niej specjalny dekorator @ddt.

@ddt

class BmiCalcDDT(unittest.TestCase):

Następnie trzeba opisać dane testowe za pomocą dekoratora @data.

# Określenie danych testowych z wykorzystaniem dekoratora @data

@data(("160", "45", "17.6", "Niedowaga"),

     ("168", "70", "24.8", "Waga normalna"),

     ("181", "89", "27.2", "Nadwaga"))

@unpack

Gdy uruchomimy test, Python odczyta krotki danych i przekaże do testu po kolei każdy wiersz.

Rozdział 8.
Korzystanie ze wzorca Page Object

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Korzystanie z klasy PageFactory do udostępniania elementów na stronie.

Korzystanie z klasy PageFactory do udostępniania operacji na stronie.

Korzystanie z klasy LoadableComponent.

Implementacja zagnieżdżonych egzemplarzy klasy Page Object.

Implementacja modelu Page Object w środowisku .NET.

Implementacja modelu Page Object w języku Python.

Implementacja modelu Page Object w Ruby z wykorzystaniem gemu page-object.

Wprowadzenie

Utworzenie łatwego do utrzymania kodu automatyzacji to jeden z kluczy do udanego projektu automatyzacji testów. Kod automatyzacji testów powinien być traktowany jako kod produkcyjny, a podczas jego rozwijania muszą być stosowane standardy i wzorce właściwe dla wytwarzania kodu takiego typu.

Podczas tworzenia testów Selenium WebDriver można używać wzorca Page Object. Stosowanie go pomaga usprawnić testy: poprawia łatwość ich utrzymania, minimalizuje powielanie kodu, buduje warstwę abstrakcji i ukrywa wewnętrzne szczegóły implementacji.

Dzięki stosowaniu zasad programowania obiektowego można tworzyć klasy, które spełniają rolę interfejsu do strony WWW w aplikacji oraz modelują jej właściwości i zachowanie. To pomaga w tworzeniu warstwy separacji pomiędzy kodem testu a kodem specyficznym dla strony, ponieważ ukrywa implementacje techniczne — na przykład kod odpowiedzialny za identyfikowanie elementów na stronie, układ strony i inny kod tego rodzaju. Wzorzec projektowy Page Object zapewnia interfejs, z pomocą którego test może operować na stronie w sposób przypominający użytkownika korzystającego ze strony, ale ukrywa jego mechanizmy wewnętrzne. Jeśli na przykład zbudujemy wzorzec Page Object dla strony logowania, uzyskamy metodę logowania, która pobierze nazwę użytkownika oraz hasło i skieruje użytkownika do głównej strony aplikacji. Test nie musi zajmować się sposobem korzystania z kontrolek wejściowych na stronie logowania, szczegółami lokalizatorów i temu podobnym detalom. Wystarczy, że utworzy egzemplarz obiektu strony logowania i wywoła metodę logowania, przekazując do niej nazwę użytkownika i hasło. Testy powinny wykorzystywać obiekty strony na wysokim poziomie, tak by jakakolwiek zmiana w układzie lub atrybutach zastosowana do pól strony nie zablokowała możliwości uruchomienia testu.

W tym rozdziale opisałem receptury budowania testów z wykorzystaniem modelu Page Object oraz projektów powiązanych.

Korzystanie z klasy PageFactory do udostępniania elementów na stronie

Aby zaimplementować wzorzec Page Object w testach, należy utworzyć klasę Page Object dla każdej testowanej strony. Żeby na przykład przetestować aplikację Kalkulator BMI, należy zdefiniować klasę kalkulatora BMI, która udostępni dla testu wewnętrzne mechanizmy strony kalkulatora BMI, tak jak pokazano na poniższym diagramie. Można to zrobić za pomocą klasy PageFactory z interfejsu API Selenium WebDriver.

Przygotuj się

Przed udostępnieniem elementów strony należy wykonać następujące czynności.

Zidentyfikować lokalizatory, które będą konieczne do jednoznacznego wyszukiwania wymaganych elementów strony.

Zdefiniować strukturę pakietów i klas dla strony.

Jak to zrobić?

Spróbujmy zaimplementować test korzystający ze wzorca Page Object dla strony Kalkulator BMI za pośrednictwem klasy PageFactory. Aby to zrobić, wykonaj następujące czynności.

W celu utworzenia logicznej grupy zdefiniuj i utwórz pakiet zawierający wszystkie obiekty stron aplikacji. Przykładowo w celu zdefiniowania wszystkich obiektów na stronie należy utworzyć pakiet seleniumcookbook.tests.pageobjects.

Utwórz nowy plik klasy Javy. Nadaj tej klasie nazwę na podstawie nazwy strony aplikacji, którą będziemy testować. Przykładowo w przypadku tworzenia obiektu strony dla aplikacji kalkulatora BMI klasa może mieć nazwę BmiCalcPage. Jest to aplikacja składająca się z pojedynczej strony. Plik klasy Javy będzie zawierać następujący kod.

package com.secookbook.examples.chapter08.pageobjects;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.PageFactory;

public class BmiCalcPage {

}

Zdefiniuj elementy strony kalkulatora BMI jako zmienne egzemplarza klasy BmiCalcPage utworzonego w kroku 1. Wykorzystaj w roli nazw tych zmiennych atrybuty name i id w następujący sposób.

private WebElement heightCMS;

private WebElement weightKg;

private WebElement Calculate;

private WebElement bmi;

@FindBy(id = "bmi_category")

private WebElement bmiCategory;

private WebDriver driver;

Dodaj do klasy BmiCalcPage konstruktor, który w celu zainicjowania elementów w klasie będzie wywoływać metodę PageFactory.initElements(). Mówiąc inaczej, zmapuj elementy do zmiennych w klasie BmiCalcPage w następujący sposób.

public BmiCalcPage(WebDriver driver) {

    this.driver = driver;

    PageFactory.initElements(driver, this);

}

Dla elementów tekstowych do wprowadzania wzrostu, wagi oraz dla przycisku Oblicz dodaj metody dostępowe. Wtedy będzie można ustawiać wartości albo wykonywać takie działania jak kliknięcie przycisku.

public void setHeight(String height) {

    heightCMS.sendKeys(height);

}

public void setWeight(String weight) {

    weightKg.sendKeys(weight);

}

public void calculateBmi() {

    Calculate.click();

}

Dodaj również metodę dostępową w celu odczytywania wartości z pól współczynnika Bmi i kategorii Bmi, tak jak pokazano w poniższym kodzie.

public String getBmi() {

    return bmi.getAttribute("value");

}

public String getBmiCategory() {

    return bmiCategory.getAttribute("value");

}

Na koniec utwórz test, który wykorzysta klasę BmiCalcPage do przetestowania strony Kalkulator BMI w następujący sposób.

package com.secookbook.examples.chapter08.tests;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import static org.junit.Assert.*;

import com.secookbook.examples.chapter08.pageobjects.BmiCalcPage;

public class BmiCalculatorTests {

    private WebDriver driver;

    @Before

    public void setUp() {

        driver = new ChromeDriver();

        driver.get("http://cookbook.seleniumacademy.com/bmicalculator.html");

    }

    @Test

    public void testBmiCalculation() {

        // Utworzenie egzemplarza klasy reprezentującej stronę Kalkulator BMI

        // i przekazanie do niego sterownika

        CopyOfBmiCalcPage bmiCalcPage = new CopyOfBmiCalcPage(driver);

        // Ustawienie wzrostu

        bmiCalcPage.setHeight("181");

        // Ustawienie wagi

        bmiCalcPage.setWeight("80");

        // Kliknięcie przycisku Oblicz

        bmiCalcPage.calculateBmi();

        // Zweryfikowanie wartości współczynnika BMI i kategorii BMI

        assertEquals("24.4", bmiCalcPage.getBmi());

        assertEquals("Waga normalna", bmiCalcPage.getBmiCategory());

}

@After

public void tearDown() {

    driver.quit();

}

Jak to działa?

Kiedy zastosujemy wzorzec Page Object i klasę PageFactory, elementy strony Kalkulator BMI są dostępne dla testu za pośrednictwem klasy BmiCalcPage. W związku z tym test nie musi bezpośrednio sięgać do wewnętrznych elementów strony.

Gdy zainicjujemy obiekt strony za pomocą klasy PageFactory należącej do klasy BmiCalcPage, obiekt klasy PageFactory wyszuka elementy na stronie na podstawie atrybutów name lub id i spróbuje dopasować nazwy obiektu WebElement zadeklarowane wewnątrz klasy BmiCalcPage w następujący sposób.

public BmiCalcPage(WebDriver driver) {

    PageFactory.initElements(driver, this);

}

Metoda initElements() pobiera obiekt sterownika utworzonego w teście i inicjuje elementy zadeklarowane w klasie BmiCalcPage. Później można bezpośrednio wywoływać metody tych elementów w następujący sposób.

// Ustawienie wzrostu

bmiCalcPage.setHeight("181");

// Ustawienie wagi

bmiCalcPage.setWeight("80");

// Kliknięcie przycisku Oblicz

bmiCalcPage.calculateBmi();

Adnotacje @FindBy

Wyszukiwanie elementów z wykorzystaniem atrybutów name lub id nie zawsze działa. Czasami trzeba zastosować zaawansowane strategie lokalizatorów, na przykład selektory XPath lub CSS. Za pomocą adnotacji @FindBy można zlokalizować elementy klasy PageFactory w następujący sposób.

@FindBy (id = "heightCMS")

public WebElement heightField;

Zadeklarowaliśmy publiczną zmienną składową reprezentującą wzrost, a w celu wyszukania tego elementu na stronie skorzystaliśmy z adnotacji @FindBy, podając identyfikator w roli lokalizatora.

Atrybut CacheLookUp

Wada używania adnotacji @FindBy polega na tym, że za każdym razem, gdy wywołujemy metodę obiektu WebElement, sterownik musi ponownie znaleźć element na bieżącej stronie. Jest to przydatne w aplikacjach, w których elementy są ładowane dynamicznie, lub w aplikacjach, w których są powszechnie stosowane techniki AJAX.

Jednak w aplikacjach, o których wiemy, że elementy będą zawsze dostępne na stronie i pozostaną takie same bez zmian, przydatne byłoby przeniesienie elementów po ich pierwszym znalezieniu do pamięci podręcznej. Aby to osiągnąć, skorzystamy z adnotacji @CacheLookUp wraz z adnotacją @FindBy w następujący sposób.

@FindBy (id = "heightCMS")

@CacheLookup

public WebElement heightField;

Jest to polecenie dla metody PageFactory.initElements(), która ma zbuforować elementy w pamięci podręcznej po ich wstępnym zlokalizowaniu. Gdy elementy są wykorzystywane często, ich umieszczenie w pamięci podręcznej powoduje, że testy działają szybciej.

Korzystanie z klasy PageFactory do udostępniania operacji na stronie

W poprzedniej recepturze utworzyliśmy klasę BmiCalcPage dostarczającą do testu elementy strony Kalkulator BMI. Wraz z elementami zdefiniowane zostały na stronie operacje lub zachowania. W aplikacji Kalkulator BMI oblicza się współczynnik BMI po wprowadzeniu wartości wzrostu i wagi. Zamiast wywoływania pojedynczych elementów i operacji można utworzyć operację o nazwie calculateBmi i wywołać ją bezpośrednio w teście.

W tej recepturze postaram się udoskonalić klasę BmiCalcPage. Zamiast elementów zastosuję operacje, które są dostępne na stronie, oraz kilka typowych właściwości. Przeniosę również egzemplarz klasy WebDriver z testu do klasy BmiCalcPage, aby test stał się generyczny.

Przygotuj się

Zidentyfikuj operacje, które będą potrzebne w teście i mogą być udostępniane ze strony. W tej recepturze skorzystamy z klasy BmiCalcPage, którą utworzyliśmy w poprzedniej recepturze.

Jak to zrobić?

Spróbujmy zmodyfikować klasę BmiCalcPage utworzoną w poprzedniej recepturze i trochę ją zrefaktoryzować, tak by dostarczyć do testu niektóre operacje i właściwości. W tym celu wykonaj następujące czynności.

Dla lepszej hermetyzacji przekształć pola reprezentujące elementy strony wewnątrz klasy BmiCalPage na prywatne. Ponadto dodaj zmienną egzemplarza klasy WebDriver i zmienną łańcuchową reprezentującą adres URL strony w następujący sposób.

package com.secookbook.examples.chapter08.tests;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.PageFactory;

public class BmiCalcPage {

    private WebElement heightCMS;

    private WebElement weightKg;

    private WebElement Calculate;

    private WebElement bmi;

    private WebElement bmi_category;

    private WebDriver driver;

    private String url = "http://dl.dropbox.com/u/55228056/

bmicalculator.html";

}

Zaktualizuj konstruktor klasy BmiCalcPage, tak aby inicjował egzemplarz klasy WebDriver, w następujący sposób.

public BmiCalcPage() {

    driver = new ChromeDriver();

    PageFactory.initElements(driver, this);

}

Dodaj do klasy BmiCalcPage metody load() i close() w poniższy sposób.

public void load() {

    this.driver.get(url);

}

public void close() {

    this.driver.close();

}

Dodaj do klasy BmiCalcPage metodę calculateBmi() o następującej treści.

public void calculateBmi(String height, String weight) {

    heightCMS.sendKeys(height);

    weightKg.sendKeys(weight);

    Calculate.click();

}

Dodaj do klasy BmiCalcPage metody getBmi i getBmiCategory() w poniższy sposób.

public String getBmi() {

return bmi.getAttribute("value");

}

public String getBmiCategory() {

return bmi_category.getAttribute("value");

}

Na koniec utwórz test, który w celu przetestowania strony Kalkulator BMI wykorzystuje metody zdefiniowane w klasie BmiCalcPage w następujący sposób.

package com.secookbook.examples.chapter08.tests;

import org.junit.Test;

import static org.junit.Assert.*;

import seleniumcookbook.tests.pageobjects.*;

public class BmiCalculatorTests {

@Test

public void testBmiCalculation()

{

    // Utworzenie egzemplarza klasy reprezentującej stronę Kalkulator BMI

    // i przekazanie do niego sterownika

    BmiCalcPage bmiCalcPage = new BmiCalcPage();

    // Otwarcie strony Kalkulator BMI

    bmiCalcPage.load();

    // Obliczenie współczynnika BMI poprzez przekazanie wartości wzrostu i wagi

    bmiCalcPage.calculateBmi("181", "80");

    // Zweryfikowanie wartości współczynnika BMI i kategorii BMI

    assertEquals("24.4", bmiCalcPage.getBmi());

    assertEquals("Waga normalna", bmiCalcPage.getBmiCategory());

    // Zamknięcie strony Kalkulator BMI

    bmiCalcPage.close();

}

Jak to działa?

W tym przykładzie klasa BmiCalcPage definiuje różne metody do ładowania strony, zamykania strony, obliczeń oraz zapewnia dostęp do elementów jako właściwości. To upraszcza tworzenie testów, ponieważ wprowadza warstwę abstrakcji i ukrywa wewnętrzne elementy strony, a udostępnia tylko operacje i pola strony potrzebne do testowania.

W przypadku zmiany struktury lub zachowania strony refaktoryzacji będzie wymagała tylko klasa BmiCalcPage, natomiast testy pozostaną bez zmian.

W tym przykładzie utworzyliśmy metodę load(), która nawiguje po aplikacji za pomocą adresu URL w następujący sposób.

public void load() {

    this.driver.get(url);

}

Stosując takie samo podejście, można również udostępniać elementy jako właściwości dające dostęp tylko do specyficznych atrybutów, na przykład value, zamiast udostępniania pełnych elementów. Przykładowo metoda getBmi() dostarcza do testu tylko atrybut value etykiety bmi.

public String getBmi() {

    return bmi.getAttribute("value");

}

Selenium WebDriver zapewnia bardzo schludny i czysty sposób implementowania modelu Page Object.

Korzystanie z klasy LoadableComponent

Obiekty wzorca Page Object można zaimplementować za pomocą klasy LoadableComponent frameworka Selenium WebDriver. Pomaga to w budowaniu solidnych obiektów reprezentacji stron, które dostarczają standardowy sposób ładowania strony oraz ułatwiają debugowanie problemów związanych z ładowaniem.

W tej recepturze jeszcze bardziej zrefaktoryzuję klasę BmiCalcPage utworzoną w poprzednich recepturach i rozszerzę ją w taki sposób, by stała się komponentem ładowalnym.

Przygotuj się

W tej recepturze skorzystamy z klasy BmiCalcPage, którą utworzyliśmy w poprzedniej recepturze.

Jak to zrobić?

Aby zaimplementować wzorzec Page Object jako klasę LoadableComponent, musimy zadbać o to, by klasa ta dziedziczyła po klasie bazowej LoadableComponent, tak jak pokazano w poniższym przykładzie.

package com.secookbook.examples.chapter08.pageobjects;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.PageFactory;

import org.openqa.selenium.support.ui.LoadableComponent;

import static org.junit.Assert.*;

public class BmiCalcPage extends LoadableComponent<BmiCalcPage> {

    private WebElement heightCMS;

    private WebElement weightKg;

    private WebElement calculate;

    private WebElement bmi;

    private WebElement bmiCategory;

    private WebDriver driver;

    private String url ="http://cookbook.seleniumacademy.com/bmicalculator.html";

    private String title = "Kalkulator BMI";

    public BmiCalcPage(WebDriver driver) {

        this.driver = driver;

    PageFactory.initElements(driver, this);

}

@Override

protected void load() {

    this.driver.get(url);

}

    @Override

    protected void isLoaded() throws Error {

         assertTrue("Strona Kalkulator Bmi nie została załadowana",

            driver.getTitle().equals(title));

    }

    public void calculateBmi(String height, String weight) {

        heightCMS.sendKeys(height);

        weightKg.sendKeys(weight);

        calculate.click();

    }

    public String getBmi() {

        return bmi.getAttribute("value");

    }

    public String getBmiCategory() {

        return bmiCategory.getAttribute("value");

    }

}

Na koniec, w treści testu zastąpimy wywołanie metody bmiCalcPage.load() metodą bmiCalcPage.get(), tak jak pokazano poniżej.

package com.secookbook.examples.chapter08.tests;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import static org.junit.Assert.*;

import com.secookbook.examples.chapter08.pageobjects.BmiCalcPage;

public class BmiCalculatorTests {

    private WebDriver driver;

    @Before

    public void setUp() {

        driver = new ChromeDriver();

    }

    @Test

    public void testBmiCalculation() {

        // Utworzenie egzemplarza klasy reprezentującej stronę Kalkulator BMI

        // i przekazanie do niego sterownika

        BmiCalcPage bmiCalcPage = new BmiCalcPage(driver);

        // Otwarcie strony Kalkulator BMI

        bmiCalcPage.get();

        // Obliczenie współczynnika BMI poprzez podanie wartości wzrostu i wagi

        bmiCalcPage.calculateBmi("181", "80");

        // Zweryfikowanie wartości współczynnika BMI i kategorii BMI

        assertEquals("24.4", bmiCalcPage.getBmi());

        assertEquals("Waga normalna", bmiCalcPage.getBmiCategory());

    }

    @After

    public void tearDown() {

        driver.quit();

    }

}

Jak to działa?

Rozszerzenie obiektu modelu Page Object o klasę bazową LoadableComponent umożliwiło przesłonięcie metod load() i isLoaded() klasy BmiCalcPage.

Metoda load() załaduje adres URL strony, której implementację zamknęliśmy we wzorcu Page Object. Kiedy tworzymy egzemplarz obiektu Page Object, w teście wywołujemy metodę get() klasy BmiCalcPage, która z kolei wywołuje metodę load() w następujący sposób.

BmiCalcPage bmiCalcPage = new BmiCalcPage(driver);

bmiCalcPage.get();

Metoda isLoaded() sprawdza, czy za pomocą metody load() została załadowana właściwa strona.

Implementacja zagnieżdżonych egzemplarzy klasy Page Object

Do tej pory omówiłem bardzo prostą implementację modelu Page Object dla jednostronicowej aplikacji webowej. Dla uproszczenia testowania model Page Object można wykorzystać do implementacji obiektów stron w złożonych aplikacjach webowych.

W tej recepturze utworzę model Page Object dla funkcji wyszukiwania w aplikacji e-commerce spod adresu http://demo.magentocommerce.com/. Model Page Object można zaimplementować nawet wtedy, kiedy określona funkcjonalność nie jest samodzielną stroną.

Każda strona aplikacji zapewnia użytkownikowi możliwość wyszukiwania produktów w witrynie. W tym celu należy wprowadzić kwerendę, po czym kliknąć przycisk Search. Po przesłaniu kwerendy wyszukiwania aplikacja zwraca nową stronę z listą produktów spełniających kryteria kwerendy wyszukiwania. Stosując podejście opisane w tej recepturze, można zbudować bardziej modularny framework, podobny do zaprezentowanego na poniższym diagramie.

W tej recepturze utworzę zagnieżdżoną hierarchię, w której klasa HomePage będzie implementować klasę Search, a ta z kolei będzie zwracać do testu obiekt klasy SearchResults.

Przygotuj się

W celu zbudowania zagnieżdżonego zbioru komponentów musimy zidentyfikować stronę oraz logiczne relacje między stronami.

Jak to zrobić?

Aby zaimplementować zagnieżdżone egzemplarze wzorca Page Object, wykonaj następujące czynności.

Utwórz klasę Browser, która dostarczy statyczny i wspólny egzemplarz WebDriver dla wszystkich stron, w następujący sposób.

package com.secookbook.examples.chapter08.pageobjects;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

public class Browser {

    private static WebDriver driver = new ChromeDriver();

    public static WebDriver driver() {

        return driver;

    }

    public static void open(String url) {

        driver.get(url);

    }

    public static void close() {

        driver.quit();

    }

}

Utwórz klasę HomePage, która umożliwia testowi przechodzenie do głównej strony aplikacji. Daje ona również dostęp do klasy Search współdzielonej przez różne strony aplikacji. Oto treść klasy HomePage.

package com.secookbook.examples.chapter08.pageobjects;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.support.PageFactory;

import org.openqa.selenium.support.ui.LoadableComponent;

import static org.junit.Assert.*;

public class HomePage extends LoadableComponent<HomePage> {

    private WebDriver driver;

    static String url = "http://demo.magentocommerce.com/";

    private static String title = "Madison Island";

    public HomePage(WebDriver driver) {

        this.driver = driver;

        PageFactory.initElements(driver, this);

    }

    @Override

    public void load() {

        Browser.open(url);

    }

    @Override

    public void isLoaded() {

        assertEquals(title, driver.getTitle());

    }

    public Search Search() {

        Search search = new Search(driver);

        return search;

    }

}

Utwórz klasę Search. Klasa udostępnia wszystkie obiekty modelu Page Object umożliwiające wyszukiwanie produktów w aplikacji. Oto jej treść.

package com.secookbook.examples.chapter08.pageobjects;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.PageFactory;

public class Search {

    private WebDriver driver;

    private WebElement search;

    @FindBy(css = "button.button")

    private WebElement searchButton;

    public Search(WebDriver driver) {

        this.driver = driver;

        PageFactory.initElements(driver, this);

    }

    public SearchResults searchInStore(String query) {

        search.sendKeys(query);

        searchButton.click();

        return new SearchResults(driver, query);

    }

}

Utwórz klasę SearchResult zagnieżdżoną wewnątrz klasy Search. Klasa SearchResult reprezentuje strony wyników zwracane po przesłaniu przez użytkownika kwerendy wyszukiwania, zgodnie z wcześniejszym opisem. Klasa SearchResult zapewnia również dostęp do klasy Search, dzięki czemu użytkownicy mogą ponownie skorzystać z wyszukiwania dla innej kwerendy. Oto treść klasy SearchResult.

package com.secookbook.examples.chapter08.pageobjects;

import java.util.ArrayList;

import java.util.List;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.PageFactory;

import org.openqa.selenium.support.ui.LoadableComponent;

import static org.junit.Assert.*;

public class SearchResults extends LoadableComponent<SearchResul

ts> {

    private WebDriver driver;

    private String query;

    public SearchResults(WebDriver driver, String query) {

        this.driver = driver;

        PageFactory.initElements(driver, this);

    }

    @Override

    public void isLoaded() {

        assertEquals("Search results for: '" + this.query +"'", driver.getTitle());

    }

    public List<String> getProducts() {

        List<String> products = new ArrayList<String>();

        List<WebElement> productList = driver.findElements(

            By.cssSelector("ul.products-grid > li"));

        for (WebElement item : productList) {

            products.add(item.findElement(By.cssSelector("h2 > a")).getText());

        }

        return products;

    }

    public Search Search() {

        Search search = new Search(driver);

        return search;

    }

    @Override

    protected void load() {

        // TODO Wygenerowana automatycznie namiastka metody

    }

}

Na koniec utworzymy test, który sprawdza funkcjonalność wyszukiwania z głównej strony aplikacji, tak jak pokazano poniżej.

package com.secookbook.examples.chapter08.tests;

import org.junit.Test;

import com.secookbook.examples.chapter08.pageobjects.Browser;

import com.secookbook.examples.chapter08.pageobjects.HomePage;

import com.secookbook.examples.chapter08.pageobjects.

SearchResults;

import static org.junit.Assert.*;

public class SearchTest {

    @Test

    public void testProductSearch() {

        try {

            // Utworzenie egzemplarza strony głównej

            HomePage homePage = new HomePage(Browser.driver());

            // Przejście do strony głównej

            homePage.get();

            // Wyszukanie frazy 'phones', metoda searchInStore zwróci obiekt

            // SearchResults

            SearchResults searchResult = homePage.Search().

searchInStore(

                "phones");

            // Sprawdzenie, czy to wyszukiwanie zwróciło 2 produkty

            assertEquals(2, searchResult.getProducts().size());

            assertTrue(searchResult.getProducts().contains(

                "MADISON OVEREAR HEADPHONES"));

        } finally {

            Browser.close();

        }

    }

}

Jak to działa?

Test rozpoczyna się od utworzenia egzemplarza klasy HomePage. Jak można zauważyć, strona główna aplikacji zapewnia użytkownikowi możliwość wyszukiwania w witrynie. W podobny sposób klasa HomePage zapewnia zdolność do wyszukiwania przy użyciu klasy Search, tak jak pokazano poniżej.

HomePage homePage = new HomePage(Browser.driver());

homePage.get();

SearchResults searchResult = homePage.Search().searchInStore(

    "phones");

Metoda searchInStore() klasy Search pobiera argument w postaci ciągu kwerendy i komunikuje się z formularzem wyszukiwania w celu przesłania zapytania. Metoda zwraca klasę SearchResults jako stronę. Klasa SearchResults za pomocą metody getProducts()zwraca do testu listę produktów, które spełniają kryteria kwerendy, w następujący sposób.

public List<String> getProducts() {

    List<String> products = new ArrayList<String>();

    List<WebElement> productList = driver.findElements(

        By.cssSelector("ul.products-grid > li"));

    for (WebElement item : productList) {

        products.add(item.findElement(By.cssSelector("h2 > a")).getText());

    }

    return products;

}

Test może zweryfikować liczbę produktów, a także ich nazwy, poprzez sprawdzenie wartości zwracanej przez metodę getProducts(), w następujący sposób.

assertEquals(2, searchResult.getProducts().size());

assertTrue(searchResult.getProducts().contains("MADISON OVEREAR HEADPHONES"));

Używając zagnieżdżonych obiektów modelu Page Object, można zbudować logiczny łańcuch stron w aplikacji oraz stworzyć solidny i łatwy do utrzymania framework automatyzacji testów.

Zobacz też

Receptura „Korzystanie z klasy LoadableComponent”.

Implementacja modelu Page Object w środowisku .NET

Selenium WebDriver, podobnie jak w dowiązaniach Javy, dostarcza klasę PageFactory w dowiązaniach .NET. Za pomocą tej klasy można zaimplementować model Page Object.

W tej recepturze zaimplementuję model Page Object dla strony Kalkulator BMI, korzystając z klasy PageFactory w języku C#.

Przygotuj się

Należy określić lokalizatory, które będą potrzebne do unikatowego zlokalizowania elementów.

Jak to zrobić?

Aby zaimplementować model Page Object w .NET, wykonaj następujące czynności.

Zdefiniuj klasę modelu Page Object poprzez utworzenie nowej klasy C# o nazwie strony. W tym przykładzie utworzymy obiekt strony dla aplikacji Kalkulator BMI.

using System;

using OpenQA.Selenium;

using OpenQA.Selenium.Chrome;

using OpenQA.Selenium.Support.PageObjects;

namespace PageFactoryTests

{

    public class BmiCalcPage

    {

        static string Url = "http://cookbook.seleniumacademy.com/bmicalculator.html";

        private static string Title = "Kalkulator BMI";

        [FindsBy(How = How.Id, Using = "heightCMS")]

        [CacheLookup]

        private IWebElement HeightField;

        [FindsBy(How = How.Id, Using = "weightKg")]

        private IWebElement WeightField;

        [FindsBy(How = How.Id, Using = "Calculate")]

        private IWebElement CalculateButton;

        [FindsBy(How = How.Name, Using = "bmi")]

        private IWebElement BmiField;

        [FindsBy(How = How.Name, Using = "bmi_category")]

        private IWebElement BmiCategoryField;

        private IWebDriver driver;

        public BmiCalcPage()

        {

            driver = new ChromeDriver(@"C:\ChromeDriver");

            PageFactory.InitElements(driver, this);

        }

        public void Load()

        {

            driver.Navigate().GoToUrl(Url);

        }

        public void Close()

        {

            driver.Close();

        }

        public bool IsLoaded

        {

            get { return driver.Title.Equals(Title); }

        }

        public void CalculateBmi(String height, String

        weight)

        {

            HeightField.SendKeys(height);

            WeightField.SendKeys(weight);

            CalculateButton.Click();

        }

        public String Bmi

        {

            get { return BmiField.GetAttribute("value"); }

        }

        public String BmiCategory

        {

            get { return BmiCategoryField.GetAttribute("value"); }

        }

    }

}

Korzystając z klasy BmiCalcPage, utworzymy test funkcji obliczeń w następujący sposób.

using NUnit.Framework;

namespace PageFactoryTests

{

    public class BmiCalcTests

    {

        [TestCase]

        public void TestBmiCalculator()

        {

            BmiCalcPage bmiCalcPage = new BmiCalcPage();

            bmiCalcPage.Load();

            Assert.IsTrue(bmiCalcPage.IsLoaded);

            bmiCalcPage.CalculateBmi("181", "80");

            Assert.AreEqual("24.4", bmiCalcPage.Bmi);

            Assert.AreEqual("Normal",

            bmiCalcPage.BmiCategory);

            bmiCalcPage.Close();

        }

    }

}

Jak to działa?

W tym przykładzie klasa BmiCalcPage dostarcza do testu różne operacje i właściwości strony Kalkulator BMI. Elementy na stronie zdefiniowano jako egzemplarze interfejsu IWebDriver. Zaimplementowano je z wykorzystaniem adnotacji FindsBy w następujący sposób.

[FindsBy(How = How.Id, Using = "heightCMS")]

private IWebElement HeightField;

Podczas inicjowania strony przez metodę PageFactory.InitElements() adnotacje te są używane do wyszukiwania elementów na stronie.

Klasa BmiCalcPage implementuje metody odpowiedzialne za otwieranie i zamykanie strony Kalkulator BMI. To dostarcza do testu wysokopoziomową abstrakcję.

Klasa BmiCalcPage implementuje również właściwość IsLoaded, która sprawdza, czy do przeglądarki została załadowana strona Kalkulator BMI. Klasa ta definiuje również właściwości reprezentujące pola Bmi i BmiCategory, które dostarczają wartości tych pól do testu zamiast pełnych elementów. Metoda ta ma następującą zawartość.

public bool IsLoaded

{

    get { return driver.Title.Equals(Title); }

}

public String Bmi

{

    get { return BmiField.GetAttribute("value"); }

}

public String BmiCategory

{

    get { return BmiCategoryField.GetAttribute("value"); }

}

Klasa dostarcza również metodę CalculateBmi(), która pobiera argumenty dla wzrostu oraz wagi i wykonuje interakcje z właściwymi elementami w celu wykonania akcji.

Implementacja modelu Page Object w Pythonie

Implementacja modelu Page Object w Pythonie jest podobna do tego, co zrobiłem wcześniej w Javie i C#. Dowiązania biblioteki Selenium WebDriver dla Pythona zapewniają obsługę obiektów wzorca Page Object.

W tej recepturze zaimplementuję model Page Object przy użyciu dowiązań do Pythona.

Przygotuj się

Przed zaimplementowaniem modelu Page Object dla dowiązań Pythona należy wykonać następujące czynności.

Zidentyfikować strony i elementy aplikacji, które będą potrzebne w testach.

Zdefiniować lokalizatory potrzebne do jednoznacznego zidentyfikowania elementów.

Jak to zrobić?

Aby zaimplementować model Page Object w Pythonie, wykonaj następujące czynności.

Zdefiniuj klasę modelu Page Object, tworząc skrypt Pythona o nazwie strony. W tym przykładzie utworzymy model Page Object dla strony głównej aplikacji Kalkulator BMI w następujący sposób.

class BmiCalcPage(object):

    def __init__(self, driver):

        self._driver = driver

        self._url = '

http://cookbook.seleniumacademy.com/bmicalculator.html'

        self._title = 'Kalkulator BMI'

    @property

    def is_loaded(self):

        return self._driver.title == self._title

    @property

    def bmi(self):

        bmi_field = self._driver.find_element_by_id('bmi')

        return bmi_field.get_attribute('value')

    @property

    def bmi_category(self):

        bmi_category_field = self._driver.find_element_by_id('bmi_category')

        return bmi_category_field.get_attribute('value')

    def open(self):

        self._driver.get(self._url)

    def calculate(self, height, weight):

        height_field = self._driver.find_element_by_id('heightCMS')

        weight_field = self._driver.find_element_by_id('weightKg')

        calc_button = self._driver.find_element_by_id('Calculate')

        height_field.send_keys(height)

        weight_field.send_keys(weight)

        calc_button.click()

Korzystając z obiektu BmiCalcPage, utworzymy test dla funkcji obliczeń w poniższy sposób.

import unittest

from selenium.webdriver.chrome.webdriver import WebDriver

from bmi_calc_page import bmicalcpage

class BmiCalcTest(unittest.TestCase):

    def test_calc(self):

        driver = WebDriver()

        bmi_calc = bmicalcpage(driver)

        bmi_calc.open()

        self.assertEqual(True, bmi_calc.is_loaded)

        bmi_calc.calculate('181','80')

        self.assertEqual('24.4', bmi_calc.bmi)

        self.assertEqual('Waga normalna', bmi_calc.bmi_category)

        driver.close()

if __name__ == '__main__':

    unittest.main()

Jak to działa?

Implementacja modelu Page Object w Pythonie jest stosunkowo prosta. Model Page Object można zaimplementować przez utworzenie klas, zdefiniowanie metody i właściwości, a następnie udostępnienie ze strony aplikacji tylko tych funkcjonalności, które są wymagane do testowania.

W tym przykładzie zdefiniowano klasę BmiCalcPage reprezentującą stronę główną aplikacji Kalkulator BMI. Klasa potrzebuje egzemplarza klasy WebDriver, za którego pośrednictwem będzie komunikować się ze stroną główną, tak jak pokazano poniżej.

class BmiCalcPage(object):

    def __init__(self, driver):

        self._driver = driver

Klasa bmiCalcPage implementuje właściwość is_loaded, która informuje testy o tym, czy do przeglądarki została załadowana strona Kalkulator BMI. Klasa ta definiuje również właściwości dla pól bmi i bmi_category dostarczające do testu wartości tylko tych pól zamiast kompletnych elementów.

@property

def is_loaded(self):

    return self._driver.title == self._title

@property

def bmi(self):

    bmi_field = self._driver.find_element_by_id('bmi')

    return bmi_field.get_attribute('value')

@property

def bmi_category(self):

    bmi_category_field = self._driver.find_element_by_id('bmi_

category')

    return bmi_category_field.get_attribute('value')

Klasa udostępnia metodę calculate(), która pobiera argumenty opisujące wzrost oraz wagę i współdziała z odpowiednimi elementami w celu wykonania działań.

Klasa BmiCalcPage implementuje również metody do otwierania i zamykania strony Kalkulator BMI. To dostarcza testom wysokopoziomową abstrakcję i ukrywa detale opisujące stronę i jej elementy. Dzięki temu testy można pisać w języku, który w pewnym sensie jest specyficzny dla dziedziny, co sprawia, że tworzenie ich staje się łatwiejsze i szybsze.

Implementacja modelu Page Object w Ruby z wykorzystaniem gemu page-object

Kiedy tworzymy testy w Ruby, do zaimplementowania w nich modelu Page Object można skorzystać z gemu page-object. Gem page-object zapewnia proste funkcje, za pomocą których można budować obiekty Page Object razem z klasą Watir WebDriver.

W tej recepturze pokażę, w jaki sposób można wykorzystać gem page-object do zaimplementowania modelu Page Object dla strony głównej Kalkulator BMI.

Przygotuj się

Należy pobrać i zainstalować gem page-object za pomocą następującego polecenia.

gem install page-object

Jak to zrobić?

Aby zaimplementować model Page Object w Ruby z wykorzystaniem gemu page-object, wykonaj następujące czynności.

Zdefiniuj klasę dla modelu Page Object, tworząc skrypt Ruby o nazwie strony. W tym przykładzie utworzymy model Page Object dla głównej strony aplikacji Kalkulator BMI i włączymy moduł page-object.

require 'page-object'

class BmiCalcPage

    include PageObject

    text_field(:height, :id => 'heightCMS')

    text_field(:weight, :id => 'weightKg')

    button(:calculate, :value => 'Calculate')

    text_field(:bmi, :id => 'bmi')

    text_field(:bmi_category, :id => 'bmi_category')

    def calculate_bmi(height, weight)

        self.height = height

        self.weight = weight

        calculate

    end

    def open()

        @browser.get 'http://cookbook.seleniumacademy.com/bmicalculator.html'

    end

end

Za pomocą obiektu klasy BmiCalcPage utwórz test dla funkcji obliczeń w następujący sposób.

require 'rubygems'

require 'watir-webdriver'

require 'test/unit'

require_relative 'bmicalcpage.rb'

class BmiCalcTest < Test::Unit::TestCase

    def test_bmi_calculation

        @driver = Selenium::WebDriver.for :chrome

        bmi_calc = BmiCalcPage.new(@driver)

        bmi_calc.open()

        bmi_calc.calculate_bmi('181','80')

        assert_equal '24.4', bmi_calc.bmi

        assert_equal 'Waga normalna', bmi_calc.bmi_category

        @driver.close()

    end

end

Jak to działa?

Możemy użyć modelu Page Object zaimplementowanego za pośrednictwem gemu page-object poprzez utworzenie egzemplarza klasy BmiCalcPage i przekazanie do konstruktora obiektu reprezentującego przeglądarkę. Resztę magii wykonuje gem page-object.

Podczas definiowania elementów musimy określić typ elementu i lokalizator w następujący sposób.

text_field(:Height, :id => 'heightCMS')

Gem page-object automatycznie dodaje do tych obiektów w czasie działania aplikacji kilka nowych metod. Przykładowo utworzyliśmy element przycisku Oblicz w modelu Page Object w następujący sposób.

button(:Calculate, :value => 'Calculate')

Gem page-object tworzy metodę o nazwie calculate, która po wywołaniu z metody calculate_bmi() będzie odpowiedzialna za kliknięcie przycisku Oblicz.

def calculate_bmi(height, weight)

    self.height = height

    self.weight = weight

    calculate

end

Więcej informacji na temat API gemu page-object można znaleźć pod adresem http://rubydoc.info/github/cheezy/page-object/master/PageObject/Accessors.

Rozdział 9.
Rozszerzanie Selenium

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Tworzenie klas rozszerzeń dla tabel webowych.

Tworzenie rozszerzenia dla widżetu tab biblioteki jQueryUI.

Implementacja rozszerzenia interfejsu WebElement w celu ustawienia wartości atrybutów elementu.

Implementacja rozszerzenia dla interfejsu WebElement w celu wyróżnienia elementów.

Utworzenie mapy obiektu dla testów Selenium.

Przechwytywanie zrzutów ekranu elementów w Selenium WebDriver.

Porównywanie obrazów w Selenium.

Mierzenie wydajności z wykorzystaniem API Navigation Timing.

Wprowadzenie

Selenium WebDriver dostarcza bardzo elastyczne i rozbudowane API pozwalające rozszerzyć funkcje i polecenia oraz wprowadzić personalizacje potrzebne do budowania skalowalnego frameworka automatyzacji testów. W tym miejscu opisano kilka ważnych receptur pozwalających na rozszerzanie Selenium WebDriver dla różnych praktycznych scenariuszy.

W tym rozdziale napiszę rozszerzenia Selenium WebDriver, które obsługują tabele webowe, mapy obiektu oraz funkcje porównywania obrazów.

Zbuduję również rozszerzenie dla kontrolki jQuery UI. Wzorzec ten można wykorzystać w celu implementacji obsługi kontrolek stosowanych w aplikacjach zewnętrznych albo kontrolek niestandardowych. Jego zastosowanie pozwala ukryć szczegóły techniczne przed testami. To sprawia, że tworzenie testów staje się znacznie łatwiejsze.

Tworzenie klas rozszerzeń dla tabel webowych

Selenium WebDriver udostępnia interfejs WebElement przeznaczony do wykorzystania z elementami HTML różnego rodzaju. Ponadto dostarcza klasy pomocnicze do obsługi elementów Select. Nie istnieje jednak wbudowana klasa do obsługi tabel webowych lub elementów <table>. W tej recepturze zaimplementuję klasę pomocniczą do obsługi tabel webowych. Za pomocą tej klasy można odczytywać właściwości oraz wykonywać niektóre podstawowe operacje na elementach tabel webowych.

Przygotuj się

Utwórz nową klasę Javy WebTable.java, którą wykorzystamy do zaimplementowania obsługi elementów table.

Jak to zrobić?

Posługując się klasą WebTable.java, zaimplementuj kod rozszerzenia dla tabel webowych. W tym celu wykonaj następujące czynności.

Dodaj konstruktor dla klasy WebTable oraz zdefiniuj metody dla setterów i getterów właściwości. Do konstruktora WebTable należy przekazać obiekt WebElement, tak jak pokazano w poniższym kodzie.

package com.secookbook.examples.chapter09;

import org.openqa.selenium.NoSuchElementException;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import java.util.List;

public class WebTable {

    private WebElement _webTable;

    public WebTable(WebElement webTable) {

        set_webTable(webTable);

    }

    public WebElement get_webTable() {

        return _webTable;

    }

    public void set_webTable(WebElement _webTable) {

        this._webTable = _webTable;

    }

}

Teraz dodaj metody do pobierania wierszy i kolumn z tabeli, tak jak pokazano w poniższym kodzie.

public int getRowCount() {

    List<WebElement> tableRows =

        _webTable.findElements(By.tagName("tr"));

    return tableRows.size();

}

public int getColumnCount() {

    List<WebElement> tableRows =

        _webTable.findElements(By.tagName("tr"));

    WebElement headerRow = tableRows.get(0);

    List<WebElement> tableCols =

        headerRow.findElements(By.tagName("td"));

    return tableCols.size();

}

Dodaj metodę getCellData() do pobierania danych z konkretnej komórki tabeli.

public String getCellData(int rowIdx, int colIdx) {

    List<WebElement> tableRows =

        _webTable.findElements(By.tagName("tr"));

    WebElement currentRow = tableRows.get(rowIdx-1);

    List<WebElement> tableCols =

        currentRow.findElements(By.tagName("td"));

    WebElement cell = tableCols.get(colIdx-1);

    return cell.getText();

}

Dodaj metodę do pobrania elementu edytora komórki. Jest to przydatne podczas pracy z komórkami, które można edytować.

public WebElement getCellEditor(int rowIdx, int colIdx, int

editorIdx) throws NoSuchElementException {

    try {

        List<WebElement> tableRows =

_webTable.findElements(By.tagName("tr"));

        WebElement currentRow = tableRows.get(rowIdx-1);

        List<WebElement> tableCols =

currentRow.findElements(By.tagName("td"));

        WebElement cell = tableCols.get(colIdx-1);

        WebElement cellEditor =

cell.findElements(By.tagName("input")).get(editorIdx);

        return cellEditor;

    } catch (NoSuchElementException e) {

        throw new NoSuchElementException("Nieudane pobranie edytora komórki

");

    }

}

Korzystając z nowo utworzonej klasy WebTable, utwórz test strony koszyka na zakupy.

package com.secookbook.examples.chapter09;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import static org.junit.Assert.*;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

public class WebTableTests {

    WebDriver driver;

    @Before

    public void setUp() {

        // Utworzenie nowego egzemplarza sterownika przeglądarki Firefox

        driver = new FirefoxDriver();

        driver.get("http:// cookbook.seleniumacademy.com/Locators.

html");

    }

    @Test

    public void testWebTableTest() {

        // Pobranie elementu table jako egzemplarza klasy WebTable za pomocą selektora CSS

        WebTable table = new WebTable(driver.findElement(By

            .cssSelector("div.cart-info table")));

        // Sprawdzenie, czy tabela ma trzy wiersze

        assertEquals(3, table.getRowCount());

        // Sprawdzenie, czy tabela ma sześć kolumn

        assertEquals(5, table.getColumnCount());

        // Sprawdzenie, czy w drugiej komórce trzeciego wiersza występuje określona wartość

        assertEquals("iPhone", table.getCellData(3, 1));

        // Pobranie edytora komórki i wprowadzenie w niej wartości

        WebElement cellEdit = table.getCellEditor(3, 3, 0);

        cellEdit.clear();

        cellEdit.sendKeys("2");

    }

    @After

    public void tearDown() {

        // Zamknięcie przeglądarki

        driver.quit();

    }

}

Jak to działa?

Klasa WebTable pobiera obiekt WebElement i rozszerza go w celu udostępnienia właściwości i operacji typowych dla tabel.

Aby pobrać liczbę wierszy z tabeli, lokalizujemy elementy <tr> z obiektu _webTable. Do tego celu wykorzystaliśmy metodę tagName() klasy By. Pobraliśmy wszystkie elementy <tr> jako listę obiektów WebElement. Za pomocą metody size() możemy dowiedzieć się, ile wierszy jest dostępnych w elemencie table.

List<WebElement> tableRows =

_webTable.findElements(By.tagName("tr"));

W podobny sposób możemy sprawdzić liczbę elementów <td> w pierwszym wierszu tabeli (zwykle jest w nim nagłówek) w celu uzyskania liczby kolumn dostępnych w elemencie table, tak jak pokazano w poniższym fragmencie kodu.

List<WebElement> tableRows =

_webTable.findElements(By.tagName("tr"));

    WebElement headerRow = tableRows.get(0);

List<WebElement> tableCols =

headerRow.findElements(By.tagName("td"));

Aby pobrać dane z określonej komórki, można skorzystać z funkcji getCellData(), która pobiera argumenty w postaci numeru wiersza i kolumny. Najpierw funkcja ta pobiera z listy element <tr>, wykorzystując argument wiersza w roli indeksu, tak jak pokazano w poniższym kodzie.

List<WebElement> tableRows =

_webTable.findElements(By.tagName("tr"));

WebElement currentRow = tableRows.get(rowIdx-1);

Następnie pobiera elementy <td> z elementu currentRow, korzystając z argumentu kolumny w roli indeksu, tak jak pokazano w poniższym kodzie.

List<WebElement> tableCols =

currentRow.findElements(By.tagName("td"));

    WebElement cell = tableCols.get(colIdx-1);

W celu pobrania tekstu funkcja wywołuje metodę getText() obiektu cell.

return cell.getText();

Często trzeba testować tabele złożone z komórek z możliwością edycji. Mogą to być dynamiczne tabele zawierające wartości z bazy danych. Metoda getCellEditor() obiektu WebTable zapewnia dostęp do pierwszego elementu wejściowego komórki. Możemy następnie użyć tego elementu wejściowego i wykonywać na nim takie działania jak Click() lub SendKeys(). Metoda getCellEditor() używa podobnego sposobu dotarcia do żądanej komórki. Następnie za pomocą strategii lokalizatora ByTagName wyszukuje wewnątrz komórki pierwszy element wejściowy — to znaczy element <td> — tak jak pokazano w poniższym kodzie.

WebElement cellEditor =

cell.findElements(By.tagName("input")).get(0);

Tworzenie rozszerzenia dla widżetu tab biblioteki jQueryUI

jQuery UI to biblioteka udostępniająca obsługę interfejsu użytkownika środowiska jQuery. Zapewnia interakcje, widżety, efekty i motywy do budowania bogatych aplikacji internetowych. jQuery UI dostarcza różne widżety UI, na przykład accordion, datepicker, slider, dialog i tab.

Widżety te są zbudowane z różnych niskopoziomowych elementów HTML, takich jak elementy DIV, listy nieuporządkowane oraz tagi do wprowadzania danych. Chociaż framework Selenium WebDriver potrafi rozpoznawać te elementy indywidualnie, możemy utworzyć rozszerzenie Selenium, dzięki któremu kontrolki te będą rozpoznawane jako natywne widżety jQuery UI. W takim przypadki można wykonywać na nich natywne operacje dostarczane przez framework jQuery.

W tej recepturze zaimplementuję obsługę widżetu tab jQuery UI.

Przygotuj się

Odwiedź witrynę http://jqueryui.com/demos/tabs/, aby dowiedzieć się więcej o widżetach tab jQuery UI. Dowiedz się, jak są implementowane oraz jakie zapewniają opcje, metody i zdarzenia.

Jak to zrobić?

W podobny sposób, jak wcześniej tworzyliśmy klasę rozszerzenia WebTable, zbudujemy klasę JQueryUITab służącą do reprezentowania w Selenium widżetu tab.

Utwórz klasę JQueryUITab z getterami i setterami właściwości.

package com.secookbook.examples.chapter09;

import org.openqa.selenium.By;

import org.openqa.selenium.JavascriptExecutor;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.internal.WrapsDriver;

import java.util.List;

public class JQueryUITab {

    private WebElement _jQueryUITab;

    public JQueryUITab(WebElement jQueryUITab) {

        set_jQueryUITab(jQueryUITab);

    }

    public WebElement get_jQueryUITab() {

        return _jQueryUITab;

    }

    public void set_jQueryUITab(WebElement _jQueryUITab) {

        this._jQueryUITab = _jQueryUITab;

    }

}

Dodaj metody do pobierania liczby kart dostępnych na widżecie tab, tak jak pokazano w poniższym kodzie. Możemy go użyć do sprawdzenia, czy widżet tab wyświetla oczekiwaną liczbę kart.

public int getTabCount() {

        List<WebElement> tabs =

_jQueryUITab.findElements(By.cssSelector(".ui-tabs-nav >

li"));

        return tabs.size();

    }

Dodaj metodę pobierającą nazwę wybranej karty. Metoda ta powinna zawierać następujący kod.

public String getSelectedTab() {

    WebElement tab = _jQueryUITab.findElement(By

        .cssSelector(".ui-tabs-nav > li[class*='ui-tabs-selected']"));

    return tab.getText();

}

Dodaj metodę do zaznaczania karty. Nazwę karty, którą chcemy zaznaczyć, przekażemy do tej metody za pomocą następującego kodu.

public void selectTab(String tabName) {

    int idx = 0;

    boolean found = false;

    List<WebElement> tabs = _jQueryUITab.findElements(By

        .cssSelector(".ui-tabs-nav > li"));

    for (WebElement tab : tabs) {

        if (tabName.equals(tab.getText().toString())) {

            WrapsDriver wrappedElement = (WrapsDriver)

_jQueryUITab;

            JavascriptExecutor driver = (JavascriptExecutor)

wrappedElement

            .getWrappedDriver();

        driver.executeScript(

                "jQuery(arguments[0]).tabs().

tabs('select',arguments[1]);",

                _jQueryUITab, idx);

            found = true;

            break;

        }

        idx++;

    }

    // Zgłoszenie wyjątku, jeśli określonej karty nie znaleziono

    if (found == false)

        throw new IllegalArgumentException("Nie można odnaleźć karty

'" + tabName

            + "'");

}

Zaimplementuj w przykładowym teście klasę JQueryUITab zawierającą następujący kod.

package com.secookbook.examples.chapter09;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.By;

import static org.junit.Assert.*;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

public class JQueryUITabWidgetTest {

    private WebDriver driver;

    @Before

    public void setUp() {

        driver = new FirefoxDriver();

        driver.get("http://

cookbook.seleniumacademy.com/jQueryUITabDemo.html");

    }

    @Test

    public void testjQueryUITabWidget() {

        JQueryUITab tab = new JQueryUITab(driver.findElement(By

            .cssSelector("div[id=MyTab][class^=ui-tabs]")));

        // Sprawdzenie, czy widżet tab zawiera trzy karty

        assertEquals(3, tab.getTabCount());

        // Sprawdzenie, czy zaznaczono zakładkę Strona główna

        assertEquals("Strona główna", tab.getSelectedTab());

        // Zaznaczenie karty Opcje i weryfikacja, czy ją zaznaczono

        tab.selectTab("Opcje");

        assertEquals ("Opcje", tab.getSelectedTab());

        // Zaznaczenie karty Administracja i sprawdzenie, czy jest zaznaczona

        tab.selectTab("Administracja");

        assertEquals("Administracja", tab.getSelectedTab());

        // Zaznaczenie karty Strona główna

        tab.selectTab("Strona główna");

}

@After

public void tearDown() {

        // Zamknięcie przeglądarki

        driver.quit();

    }

}

Jak to działa?

Przygotowaliśmy klasę JQueryUITab do wykorzystania w celu testowania widżetu tab. Do konstruktora klasy JQueryUITab przekazujemy obiekt klasy WebElement. Aby odczytać liczbę kart w widżecie tab, przekazaliśmy element tab. Wewnętrznie w widżecie tab jest zdefiniowana nieuporządkowana lista nagłówków kart. Korzystając z metody findElements() klasy By oraz strategii cssSelector(), zlokalizowaliśmy te nagłówki na liście WebElement. Liczbę kart odczytaliśmy na podstawie rozmiaru listy.

List<WebElement> tabs =

_jQueryUITab.findElements(By.cssSelector(".ui-tabs-nav > li"));

    return tabs.size();

W celu pobrania nazwy zaznaczonej karty użyliśmy podobnej metody cssSelector(), przekazując filtr do zlokalizowania elementu <li>, którego klasą jest ui-tabs-selected. Gdy zaznaczymy kartę w obrębie widżetu tab, framework jQuery wewnętrznie dodaje wszystkie te atrybuty klasy do elementu <li>, tak jak pokazano w poniższym kodzie.

WebElement tab = _jQueryUITab.findElement(By.cssSelector

(".ui-tabs-nav > li[class*='ui-tabs-selected']"));

return tab.getText();

Na koniec, by zaznaczyć kartę w widżecie tab, skorzystaliśmy z natywnych funkcji interfejsu API biblioteki jQuery. Widżet tab oferuje metodę do zaznaczania zakładki za pomocą jej indeksu. Jednak zaznaczanie zakładki na podstawie indeksu może być niewygodne. W związku z tym przekazujemy nazwę zakładki, a następnie wyznaczamy jej indeks przy użyciu następującego kodu.

int idx=0;

List<WebElement> tabs =

_jQueryUITab.findElements(By.cssSelector(".ui-tabs-nav > li"));

Iterator<WebElement> itr = tabs.iterator();

while(itr.hasNext()) {

    WebElement element = itr.next();

    if(tabName.equals(element.getText().toString()))

        break;

    idx++;

}

Następnie za pomocą obiektu JavascriptExecutor wywołujemy natywne funkcje API biblioteki jQuery i przekazujemy indeks do metody select widżetu Tab, posługując się następującym kodem.

WrapsDriver wrappedElement = (WrapsDriver) _jQueryUITab;

JavascriptExecutor driver = (JavascriptExecutor)

wrappedElement.getWrappedDriver();

    driver.executeScript("jQuery(arguments[0]).tabs().tabs('select',ar

guments[1]);",_jQueryUITab,idx);

}

Co dalej?

Stosując podobne podejście, możemy zapewnić obsługę innych widżetów w jQuery UI lub innych frameworkach interfejsu użytkownika, takich jak Yahoo UI, Doojo i GWT. W ten sposób można zaoferować wygodny i czysty sposób pracy z niestandardowymi widżetami i kontrolkami interfejsu użytkownika.

Implementacja rozszerzenia klasy WebElement w celu ustawiania wartości atrybutów elementów

Ustawianie atrybutu elementu może być przydatne w różnych sytuacjach, kiedy test ma manipulować właściwościami elementu. Przykładowo dla maskowanego pola tekstowego metoda sendKeys() może nie działać poprawnie. W takiej sytuacji ustawienie wartości pola tekstowego pomaga przezwyciężyć występujące problemy. Interfejs WebElement bezpośrednio nie dostarcza metody, która obsługiwałaby ustawianie wszystkich typów atrybutów.

W tej recepturze utworzę rozszerzenie interfejsu WebElement i dostarczę metodę ustawiającą wartość atrybutu elementu w czasie wykonywania aplikacji.

Przygotuj się

Utwórz nowy plik klasy Java WebElementExtender.java. Tej klasy użyjemy jako hosta wszystkich metod rozszerzeń elementów.

Jak to zrobić?

Do klasy WebElementExtender dodaj metodę setAttribute() w następujący sposób.

import org.openqa.selenium.JavascriptExecutor;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.internal.WrapsDriver;

    public class WebElementExtender {

        public static void setAttribute(WebElement element, String

        attributeName, String value)

        {

            WrapsDriver wrappedElement = (WrapsDriver) element;

            JavascriptExecutor driver = (JavascriptExecutor)

            wrappedElement.getWrappedDriver();

            driver.executeScript("arguments[0].setAttribute(arguments[1],

            arguments[2])", element, attributeName, value);

        }

}

Jak to działa?

W metodzie setAttribute() utworzyliśmy obiekt JavascriptExecutor. Następnie pobraliśmy obiekt WrappedDriver obiektu WebElement, na którym chcemy wywołać metodę setAttribute().

Za pomocą obiektu JavascriptExecutor wywołaliśmy metodę JavaScript setAttribute() odpowiedzialną za ustawianie wartości atrybutu elementu. W tym przykładzie przed wywołaniem metody SendKeys() zawartość elementów do wprowadzania danych jest usuwana. Można to również zrobić przez wywołanie metody clear() klasy WebElement.

WebElement email = driver.findElement(By.id("email"));

WebElementExtender.setAttribute(userName, "value", "");

userName.sendKeys("test@test.com");

Zobacz też

Receptura „Implementacja rozszerzenia interfejsu WebElement w celu wyróżniania elementów”.

Implementacja rozszerzenia interfejsu WebElement w celu wyróżniania elementów

Podczas wykonywania testów nie ma sposobu wyróżniania elementów. Wyróżnianie mogłoby pomóc w pokazaniu działań wykonywanych w przeglądarce. Zastosowanie tego mechanizmu nieco spowolni testy, ale czasami jest przydatnym narzędziem do ich debugowania.

W tej recepturze utworzymy rozszerzenie dla interfejsu WebElement, dostarczając metodę highlightElements() w czasie działania aplikacji.

Przygotuj się

Utwórz nowy plik klasy Java WebElementExtender.java. Możesz także użyć klasy utworzonej w poprzedniej recepturze.

Jak to zrobić?

Dodaj metodę highlightElement() klasy WebElementExtender w następujący sposób.

import org.openqa.selenium.JavascriptExecutor;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.internal.WrapsDriver;

public class WebElementExtender {

    public static void highlightElement(WebElement element) {

        for (int i = 0; i < 5; i++) {

            WrapsDriver wrappedElement = (WrapsDriver) element;

            JavascriptExecutor driver = (JavascriptExecutor)

            wrappedElement.getWrappedDriver();

            driver.executeScript("arguments[0].setAttribute('style',

            arguments[1]);",

                      element, "color: green; border: 2px solid

yellow;");

            driver.executeScript("arguments[0].setAttribute('style',

            arguments[1]);",

                      element, "");

        }

    }

}

Jak to działa?

W metodzie highlightElement() utworzyliśmy egzemplarz klasy JavascriptExecutor i na podstawie elementu uzyskaliśmy egzemplarz klasy WrappedDriver, na którym chcieliśmy wywołać metodę highlightElement().

Korzystając z klasy JavascriptExecutor, wywołaliśmy metodę JavaScript setAttribute(), aby ustawić wartość atrybutu style na green, a następnie z powrotem przywrócić wartość wyjściową. Zrobiliśmy to kilka razy w pętli. Podczas działania aplikacji element jest wyróżniony miganiem na zielono. Oto przykład użycia metody highlightElement().

WebElement userName = driver.findElement(By.id("username"));

WebElementExtender.highlightElement(userName);

userName.sendKeys("test_user");

Korzystanie z metody highlightElement() bardzo się przydaje podczas debugowania lub wizualizacji postępów testu.

Zobacz też

Receptura „Implementacja rozszerzenia klasy WebElement w celu ustawiania wartości atrybutów elementów”.

Utworzenie mapy obiektów dla testów Selenium

Dotychczas widzieliśmy, że do wyszukiwania elementów na stronie Selenium WebDriver API wymaga informacji lokalizatora. Kiedy piszemy duży zestaw testów, sporo informacji na temat lokalizatora dubluje się w kodzie. Gdy zwiększa się liczba testów, zarządzanie szczegółami lokalizatora staje się trudne. Jeśli zajdą jakiekolwiek zmiany w elemencie lokalizatora, trzeba znaleźć wszystkie testy, które używają tego lokalizatora, i je zaktualizować. Utrzymanie takiego kodu staje się koszmarem.

Jednym ze sposobów rozwiązania tego problemu jest skorzystanie z obiektów strony i opracowanie repozytorium stron jako klas wielokrotnego użytku.

Istnieje także inna droga do przezwyciężenia tego problemu — polegająca na zastosowaniu mapy obiektów. Mapa obiektów lub mapa interfejsu użytkownika (UI) to mechanizm, który przechowuje wszystkie lokalizatory dla zestawu testów w jednym miejscu. Pozwala to na ich łatwe modyfikowanie, w przypadku gdy w testowanej aplikacji zmienią się identyfikatory albo ścieżki do elementów GUI. Test skryptu może następnie użyć mapy obiektu w celu zlokalizowania elementów, które mają być przetestowane.

Mapy obiektów pomagają w łatwym zarządzaniu skryptami testów. Gdy trzeba wyedytować lokalizator, łatwo znaleźć centralną lokalizację, gdzie można wyszukać obiekt. To pozwala uniknąć konieczności przeszukiwania kodu testu. Poza tym pozwala to zmienić identyfikator w jednym miejscu, zamiast konieczności wprowadzania zmian w wielu miejscach skryptu testowego albo — jeszcze gorzej — w wielu skryptach testowych. Pliki mapy obiektów mogą być także przechowywane w systemie zarządzania wersjami.

W tej recepturze zaimplementuję klasę ObjectMap, w której będą przechowywane szczegóły lokalizatorów uzyskane z testów.

Przygotuj się

Skonfiguruj nowy projekt Java dla klasy ObjectMap. Klasa będzie używana w testach Selenium jako rozszerzenie odpowiedzialne za odczytywanie pliku ObjectMap.

Jak to zrobić?

Zaimplementuj mapę obiektów do przechowywania lokalizatorów używanych w teście. W tym celu wykonaj następujące kroki.

Utwórz plik właściwości o nazwie objectmap.properties. Lokalizatory zostaną dodane w formie par klucz-wartość. Część przed znakiem równości (=) będzie kluczem albo logiczną nazwą elementu, natomiast część za kluczem będzie oznaczała szczegóły lokalizatora w następującym formacie.

[nazwa_logiczna]=[typ_lokalizatora]>[wartość_lokalizatora]

Oto przykład mapy obiektów dla strony Kalkulator BMI.

height_field=name>heightCMS

weight_field=id>weightKg

calculate_button=id>Calculate

bmi_field=id>bmi

Zaimplementuj klasę ObjectMap odpowiedzialną za odczyt pliku właściwości i dostarczenie do testu informacji na temat lokalizatora.

import java.io.FileInputStream;

import java.io.IOException;

import java.util.Properties;

public class ObjectMap {

    Properties properties;

    public ObjectMap(String mapFile)

    {

        properties = new Properties();

        try {

            FileInputStream in = new FileInputStream(mapFile);

            properties.load(in);

            in.close();

        }catch (IOException e) {

            System.out.println(e.getMessage());

        }

    }

}

Dodaj do klasy ObjectMap metodę, która odczyta szczegóły lokalizatora z pliku właściwości, a następnie utworzy i zwróci lokalizator za pomocą klasy By, tak jak pokazano w poniższym kodzie.

public By getLocator(String logicalElementName) throws

Exception

{

    // Odczytanie wartości z wykorzystaniem logicznej nazwy w roli klucza

    String locator =

properties.getProperty(logicalElementName);

    // Podzielenie wartości zawierającej typ lokalizatora i jego wartość

    String locatorType = locator.split(">")[0];

String locatorValue = locator.split(">")[1];

    // Zwrócenie egzemplarza klasy By na podstawie typu lokalizatora

    if(locatorType.toLowerCase().equals("id"))

        return By.id(locatorValue);

    else if(locatorType.toLowerCase().equals("name"))

        return By.name(locatorValue);

    else if((locatorType.toLowerCase().equals("classname"))

||

    (locatorType.toLowerCase().equals("class")))

        return By.className(locatorValue);

    else if((locatorType.toLowerCase().equals("tagname")) ||

    (locatorType.toLowerCase().equals("tag")))

        return By.className(locatorValue);

    else if((locatorType.toLowerCase().equals("linktext")) ||

    (locatorType.toLowerCase().equals("link")))

        return By.linkText(locatorValue);

    else if(locatorType.toLowerCase().equals("partiallinktext"))

        return By.partialLinkText(locatorValue);

    else if((locatorType.toLowerCase().equals("cssselector"))

||

    (locatorType.toLowerCase().equals("css")))

        return By.cssSelector(locatorValue);

    else if(locatorType.toLowerCase().equals("xpath"))

        return By.xpath(locatorValue);

    else

    throw new Exception("Typ lokalizatora '" + locatorType + "'

nie został zdefiniowany!");

}

Na koniec utwórz test, który skorzysta z pliku właściwości do przechowywania informacji o lokalizatorze, przy użyciu następującego kodu.

package com.secookbook.examples.chapter09;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.WebElement;

import org.junit.*;

import static org.junit.Assert.assertEquals;

public class ObjectMapTest {

    private WebDriver driver;

    private ObjectMap map;

    @Before

    public void setUp() throws Exception {

        // Utworzenie nowego egzemplarza sterownika przeglądarki Firefox

        driver = new FirefoxDriver();

        driver.get("http:// cookbook.seleniumacademy.com/

bmicalculator.html");

    }

    @Test

    public void testBmiCalculator() {

        // Pobranie pliku mapy obiektów

        map = new

ObjectMap("src/test/resources/objectmap/objectmap.propertie

s");

        // Uzyskanie elementu reprezentującego wzrost

        WebElement height =

driver.findElement(map.getLocator("height_field"));

        ;

        height.sendKeys("181");

        // Uzyskanie elementu reprezentującego wagę

        WebElement weight =

driver.findElement(map.getLocator("weight_field"));

        weight.sendKeys("80");

        // Kliknięcie przycisku Oblicz

        WebElement calculateButton = driver.findElement(map

            .getLocator("calculate_button"));

        calculateButton.click();

        // Zweryfikowanie współczynnika BMI

        WebElement bmi =

driver.findElement(map.getLocator("bmi_field"));

        assertEquals("24.4", bmi.getAttribute("value"));

    }

    @After

    public void tearDown() throws Exception {

        // Zamknięcie przeglądarki

        driver.quit();

    }

}

Jak to działa?

Najpierw utworzyliśmy plik właściwości Javy zawierający pary klucz-wartość z logiczną nazwą elementu i wartością lokalizatora. Pliki właściwości to płaskie pliki tekstowe. Dostęp do pliku właściwości można uzyskać za pośrednictwem klasy Properties z przestrzeni nazw Properties.

properties = new Properties();

Przekazując logiczną nazwę albo klucz do metody getProperty() klasy Properties, można pobrać wartość z pary.

Metoda getLocator() korzysta z wartości zwróconej przez metodę getProperty() i zwraca do testu pasującą metodę lokalizatora By razem z wartością.

W teście utworzyliśmy egzemplarz mapy obiektu, a następnie przekazaliśmy lokalizację pliku właściwości, tak jak pokazano w poniższym kodzie.

map = new

ObjectMap("src/test/resources/objectmap/objectmap.properties");

Wartość lokalizatora przekazaliśmy do metody findElement() poprzez przekazanie logicznej nazwy (klucza) elementu do metody getLocator() klasy ObjectMap w następujący sposób.

WebElement height =

driver.findElement(map.getLocator("height_field"));

Wszystkie lokalizatory mogą być przechowywane w pojedynczym pliku mapy obiektów. Można także używać tych samych lokalizatorów w wielu testach.

Co dalej?

Mapy obiektów mogą być również tworzone w plikach XML. Przykładem mapy obiektów bazującej na XML może być poniższy kod.

<elements>

    <element name="HeightField" locator_type="name"

locator_value="heightCMS"/>

    <element name="WeightField" locator_type="id"

locator_value="weightKg"/>

    <element name="CalculateButton" locator_type="xpath"

locator_value="//input[@value='Calculate']"/>

    <element name="BmiField" locator_type="id" locator_value="bmi"/>

    <element name="BmiCategoryField" locator_type="css"

locator_value="#bmi_category"/>

    </elements>

Oto implementacja metody GetLocator() w języku C#.

public By GetLocator(string locatorName)

{

    var element = from elements in _root.Elements("element")

            where elements.Attributes("name").First().Value ==

            locatorName

            select elements;

    try

    {

        string locatorType =

element.Attributes("locator_type").First().

        Value.ToString();

        string locatorValue =

element.Attributes("locator_value").First().

        Value.ToString();

        switch (locatorType.ToLower())

        {

            case "id":

              return By.Id(locatorValue);

            case "name":

              return By.Name(locatorValue);

            case "classname":

              return By.ClassName(locatorValue);

            case "linktext":

              return By.LinkText(locatorValue);

            case "partiallinktext":

              return By.PartialLinkText(locatorValue);

            case "css":

              return By.CssSelector(locatorValue);

            case "xpath":

              return By.XPath(locatorValue);

            case "tagname":

              return By.TagName(locatorValue);

            default:

              throw new Exception("Typ lokalizatora '" + locatorType + "'

              nie jest obsługiwany!");

        }

    }

    catch (Exception)

    {

        throw new Exception("Nieudana operacja generowania lokalizatora dla '" +

        locatorName + "'");

    }

}

Podobne podejście można zastosować dla dowiązań biblioteki Selenium WebDriver do innych języków, takich jak Java, Python i Ruby.

Przechwytywanie zrzutów ekranu elementów w Selenium WebDriver

Interfejs TakesScreenshot pozwala na przechwytywanie zrzutu ekranu całej strony, bieżącego okna, widocznego fragmentu strony lub kompletnego okna pulpitu w kolejności obsługiwanej przez przeglądarkę. Nie zapewnia sposobu przechwytywania obrazu specyficznego elementu.

Korzystając z interfejsu API Java Image, możemy rozszerzyć funkcje przechwytywania ekranu interfejsu TakesScreenshot, tak aby było możliwe tworzenie zrzutów ekranu elementów WebElement.

W tej recepturze zaimplementuję metody pomocnicze do przechwytywania obrazów elementów.

Jak to zrobić?

Zaimplementujemy metodę captureElementPicture() odpowiedzialną za przechwytywanie obrazu obiektu WebElement. Do tej metody przekażemy egzemplarz klasy WebElement. Przykład zaprezentowano w poniższym kodzie.

public static File captureElementPicture(WebElement element)

        throws Exception {

    // Zrzutowanie obiektu WebElement na obiekt klasy WrapsDriver

    WrapsDriver wrapsDriver = (WrapsDriver) element;

    // Uzyskanie zrzutu całego ekranu na podstawie sterownika przekazanego obiektu WebElement

    File screen = ((TakesScreenshot) wrapsDriver.getWrappedDriver())

         .getScreenshotAs(OutputType.FILE);

    // Utworzenie egzemplarza BufferedImage na podstawie przechwyconego zrzutu ekranu

    BufferedImage img = ImageIO.read(screen);

    // Odczytanie szerokość i wysokości obiektu WebElement za pomocą metody getSize()

    int width = element.getSize().getWidth();

    int height = element.getSize().getHeight();

    // Utworzenie prostokąta z wykorzystaniem szerokości i wysokości

    Rectangle rect = new Rectangle(width, height);

    // Uzyskanie lokalizacji obiektu WebElement w obiekcie Point.

    // W ten sposób otrzymamy współrzędne X i Y obiektu WebElement

    Point p = element.getLocation();

    // Utworzenie obrazu elementu z wykorzystaniem jego lokalizacji i rozmiaru.

    // To zapewni dane obrazu specyficzne dla obiektu WebElement

    BufferedImage dest = img.getSubimage(p.getX(), p.getY(),rect.width,

        rect.height);

    // Zapisanie danych obrazu reprezentującego element w obiekcie File

    ImageIO.write(dest, "png", screen);

    // Zwrócenie obiektu File zawierającego dane obrazu

    return screen;

}

Jak to działa?

Gdy zostanie wywołana metoda captureElementPicture() z argumentem w postaci obiektu WebElement, to korzystając z klasy WrapsDriver, pobiera ona egzemplarz obiektu sterownika. Następnie za pomocą metody getScreenshotAs() interfejsu TakesScreenshot przechwytuje zrzut ekranu strony wyświetlanej w sterowniku i przekazuje lokalizację oraz rozmiar elementu. Obraz elementu zostanie przycięty na podstawie obrazu całej strony.

W poniższym przykładzie do przechwycenia obrazu elementu button skorzystano z metody captureElementPicture().

@Test

public void testElementScreenshot() {

    WebElement searchButton = driver.findElement(By.name("btnK"));

    try {

        FileUtils.copyFile(

            WebElementExtender.captureElementPicture(searchButton),

            new File("target/searchButton.png"));

    } catch (Exception e) {

        e.printStackTrace();

    }

}

Zobacz też

Receptura „Przechwytywanie zrzutów ekranu za pomocą Selenium WebDriver”, w rozdziale 4., „Korzystanie z Selenium API”.

Porównywanie obrazów w Selenium

W testach wielokrotnie musimy wykonywać porównania bazujące na obrazach. Chcemy na przykład sprawdzić, czy są wyświetlane odpowiednie ikony, zweryfikować, czy na stronach WWW są wyświetlane właściwe ilustracje, albo porównać bazowy układ ekranu z układem rzeczywistym.

Selenium WebDriver dostarcza funkcje do przechwytywania zrzutów ekranu lub obrazów z testowanych aplikacji, ale nie udostępnia funkcji do porównywania obrazów.

W tej recepturze utworzę klasę rozszerzenia odpowiedzialną za porównywanie obrazów i użyję tej klasy w testach Selenium.

Przygotuj się

Skonfiguruj nowy projekt Javy dla klasy CompareUtil. Klasa będzie używana w testach Selenium jako rozszerzenie zapewniające funkcjonalność porównywania obrazów.

Jak to zrobić?

Zaimplementujemy klasę CompareUtil z metodą do porównania dwóch plików obrazu, tak jak pokazano w poniższym kodzie.

package com.secookbook.examples.chapter09;

import java.awt.Image;

import java.awt.Toolkit;

import java.awt.image.PixelGrabber;

public class CompareUtil {

    public enum Result {

        Matched, SizeMismatch, PixelMismatch

    };

    public static Result CompareImage(String baseFile, String actualFile) {

        Result compareResult = Result.PixelMismatch;

        Image baseImage = Toolkit.getDefaultToolkit().getImage(baseFile);

        Image actualImage = Toolkit.getDefaultToolkit().getImage(actualFile);

        try {

            PixelGrabber baseImageGrab = new PixelGrabber(baseImage, 0, 0, -1,

            -1, false);

        PixelGrabber actualImageGrab = new PixelGrabber(actualImage, 0, 0,

            -1, -1, false);

        int[] baseImageData = null;

        int[] actualImageData = null;

        if (baseImageGrab.grabPixels()) {

            int width = baseImageGrab.getWidth();

            int height = baseImageGrab.getHeight();

            baseImageData = new int[width * height];

            baseImageData = (int[]) baseImageGrab.getPixels();

        }

        if (actualImageGrab.grabPixels()) {

            int width = actualImageGrab.getWidth();

            int height = actualImageGrab.getHeight();

            actualImageData = new int[width * height];

            actualImageData = (int[]) actualImageGrab.getPixels();

        }

        if ((baseImageGrab.getHeight() !=

actualImageGrab.getHeight())

            || (baseImageGrab.getWidth() !=

actualImageGrab.getWidth()))

            compareResult = Result.SizeMismatch;

        else if (java.util.Arrays.equals(baseImageData,

actualImageData))

            compareResult = Result.Matched;

        } catch (Exception e) {

            e.printStackTrace();

        }

        return compareResult;

    }

}

Jak to działa?

Do przetwarzania obrazów klasa CompareUtil używa przestrzeni nazw java.awt.Image. Metoda CompareImage() pobiera argumenty reprezentujące ścieżkę pliku bazowego oraz rzeczywisty plik. Następnie na tej podstawie tworzy egzemplarze klasy Image.

Image baseImage = Toolkit.getDefaultToolkit().getImage(baseFile);

Image actualImage = Toolkit.getDefaultToolkit().getImage(actualFile);

Na koniec używa klasy PixelGrabber w celu pobrania pikseli z tych obrazów do tablicy, tak jak pokazano w poniższym kodzie.

PixelGrabber baseImageGrab = new PixelGrabber(baseImage, 0, 0, -1,

-1, false);

PixelGrabber actualImageGrab = new PixelGrabber(actualImage, 0, 0,

-1, -1, false);

int[] baseImageData = null;

int[] actualImageData = null;

if(baseImageGrab.grabPixels()) {

    int width = baseImageGrab.getWidth();

    int height = baseImageGrab.getHeight();

    baseImageData = new int[width * height];

    baseImageData = (int[])baseImageGrab.getPixels();

}

if(actualImageGrab.grabPixels()) {

    int width = actualImageGrab.getWidth();

    int height = actualImageGrab.getHeight();

    actualImageData = new int[width * height];

    actualImageData = (int[])actualImageGrab.getPixels();

}

Obrazy są najpierw testowane pod kątem niezgodności rozmiaru, a następnie pod względem niedopasowania pikseli. Służy do tego następujący kod.

if ((baseImageGrab.getHeight() != actualImageGrab.getHeight()) ||

(baseImageGrab.getWidth() != actualImageGrab.getWidth()))

    compareResult = Result.SizeMismatch;

else if(java.util.Arrays.equals(baseImageData, actualImageData))

    compareResult = Result.Matched;

Poniższy kod jest przykładowym testem, który porównuje układ testowanej aplikacji z układem bazowym pochodzącym ze starszej wersji.

package com.secookbook.examples.chapter09;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.*;

import org.apache.commons.io.FileUtils;

import org.junit.*;

import static org.junit.Assert.*;

import java.io.File;

public class BmiCalculatorTest {

    private WebDriver driver;

    @Before

    public void setUp() throws Exception {

        // Utworzenie nowego egzemplarza sterownika przeglądarki Firefox

        driver = new FirefoxDriver();

    }

    @Test

    public void testBmiCalculatorLayout() throws Exception {

        String scrFile = "c:\\screenshot.png";

        String baseScrFile = "c:\\baseScreenshot.png";

        // Otwarcie strony Kalkulator BMI i utworzenie zrzutu ekranu strony w formie obiektu File

        driver.get("http://dl.dropbox.com/u/55228056/bmicalculator.html");

        File screenshotFile = ((TakesScreenshot) driver)

            .getScreenshotAs(OutputType.FILE);

        FileUtils.copyFile(screenshotFile, new File(scrFile));

        // Zweryfikowanie obrazu bazowego z rzeczywistym

        assertEquals(CompareUtil.Result.Matched,

            CompareUtil.CompareImage(baseScrFile, scrFile));

    }

    @After

    public void tearDown() throws Exception {

        // Zamknięcie przeglądarki

        driver.quit();

    }

}

Co dalej?

Poniższy kod jest implementacją klasy CompareUtil w języku C#. Z tej klasy możemy skorzystać do tworzenia testów Selenium z dowiązaniami .NET.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Drawing;

using System.Drawing.Imaging;

namespace CompareUtil

{

    public class CompareUtil

    {

        public enum Result { Matched, SizeMismatch, PixelMismatch

};

        public static Result CompareImage(string baseFile,

        string actualFile)

    {

        Result result = Result.Matched;

        Bitmap baseBmp = (Bitmap)Image.FromFile(baseFile);

        Bitmap actBmp = (Bitmap)Image.FromFile(actualFile);

        if (baseBmp.Size != actBmp.Size)

            result = Result.SizeMismatch;

        else

        {

            int height = Math.Min(baseBmp.Height,

actBmp.Height);

            int width = Math.Min(baseBmp.Width, actBmp.Width);

            bool are_identical = true;

            for (int x = 0; x <= width - 1; x++)

            {

                for (int y = 0; y <= height - 1; y++)

                {

                    if (baseBmp.GetPixel(x, y).Equals(actBmp.

                    GetPixel(x, y)))

                    {

                    }

                    else

                    {

                        are_identical = false;

                    }

                }

            }

            if (are_identical == true)

                result = Result.Matched;

            else

                result = Result.PixelMismatch;

            }

            return result;

        }

    }

}

Zobacz też

Receptura „Przechwytywanie zrzutów ekranu za pomocą Selenium WebDriver”, w rozdziale 4., „Korzystanie z Selenium API”,

Receptura „Przechwytywanie zrzutów ekranu elementów w Selenium WebDriver”.

Mierzenie wydajności z wykorzystaniem API Navigation Timing

Pomiar i optymalizacja wydajności po stronie klienta mają zasadnicze znaczenie dla bezproblemowego korzystania z aplikacji. Wydajność jest kluczową cechą aplikacji web 2.0 korzystających z technik AJAX.

Przechwytywanie istotnych informacji, takich jak czas ładowania strony, renderowania elementów i uruchamiania kodu JavaScript, pomaga w zidentyfikowaniu obszarów, gdzie wydajność jest niska, i w zoptymalizowaniu ogólnej wydajności po stronie klienta.

Czasami obniżenie wydajności jest spowodowane również korzystaniem z kontrolek, zdjęć i zawartości multimedialnych pochodzących od zewnętrznych dostawców. Za pomocą biblioteki Selenium WebDriver razem z różnymi innymi narzędziami możemy mierzyć wydajność i eliminować zawartość, która powoduje jej obniżenie.

Navigation Timing to interfejs API języka JavaScript, zgodny ze standardem W3C, służący do pomiaru wydajności aplikacji webowych. API zapewnia prosty sposób uzyskania dokładnych i szczegółowych statystyk czasowych dla zdarzeń nawigowania po stronach oraz ich ładowania. Jest dostępny dla przeglądarek Internet Explorer 9, Google Chrome, Firefox oraz przeglądarek bazujących na WebKit.

Interfejs API jest dostępny w języku JavaScript za pośrednictwem właściwości interfejsu timing obiektu window.performance.

Każdy atrybut performance.timing pokazuje czas zdarzenia nawigacji, gdy zażądano strony lub gdy zmierzono czas ładowania strony. Czas jest mierzony w milisekundach od północy 1 stycznia 1970 roku (UTC). Wartość zero oznacza, że zdarzenie nie wystąpiło.

Kolejność zdarzeń performance.timing przedstawiono na poniższym diagramie z projektu Navigation Timing.

Przygotuj się

Znajdź test, w którym chcesz zmierzyć wydajność, jak również zdecyduj, jakie liczniki wydajności chcesz zmierzyć.

Jak to zrobić?

Aby uzyskać metryki czasowe, musimy uzyskać dostęp do obiektu window.performance za pomocą obiektu JavascriptExecutor. Przykład zaprezentowano w poniższym kodzie.

// Otwarcie strony aplikacji mobilnej Kalkulator BMI

driver.get("http://

cookbook.seleniumacademy.com/bmicalculator.html");

JavascriptExecutor js = (JavascriptExecutor) driver;

// Uzyskanie czasu zakończenia zdarzenia ładowania

long loadEventEnd = (Long) js.executeScript("return

window.performance.timing.loadEventEnd;");

// Uzyskanie czasu rozpoczęcia zdarzenia nawigacji

long navigationStart = (Long) js.executeScript("return

window.performance.timing.navigationStart;");

// Różnica pomiędzy czasem końca zdarzenia ładowania i rozpoczęcia nawigacji to

// czas ładowania strony

System.out.println("Czas ładowania strony wynosi " + (loadEventEnd -

navigationStart)/1000 + " sekund.");

Kompletny test można znaleźć w sekcji NavTimingDemo.java w przykładowym kodzie dołączonym do książki.

Jak to działa?

Zgodnie z kodem zaprezentowanym powyżej obiekt window.performance dostarcza metryki wydajnościowe dostępne w obrębie obiektu reprezentującego okno przeglądarki. Do pobrania takiej metryki należy skorzystać z kodu JavaScript. Można to zrobić za pomocą następującego kodu.

JavascriptExecutor js = (JavascriptExecutor) driver;

// Uzyskanie czasu zakończenia zdarzenia ładowania

long loadEventEnd = (Long) js.executeScript("return

window.performance.timing.loadEventEnd;");

W zaprezentowanym przykładzie odczytaliśmy czas zdarzenia rozpoczęcia ładowania (loadEventEnd) oraz czas rozpoczęcia nawigacji (navigationEventStart). Obliczenie różnicy pomiędzy nimi daje nam czas ładowania strony.

Rozdział 10.
Testowanie aplikacji webowych HTML5

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Automatyzowanie odtwarzacza wideo HTML5.

Automatyzowanie interakcji z elementem HTML5 canvas.

Web storage — testowanie lokalnego magazynu.

Web storage — testowanie magazynu sesji.

Czyszczenie magazynu lokalnego i magazynu sesji.

Wprowadzenie

Popularność HTML5 rośnie, a obsługę tego standardu zapewniają wszystkie główne przeglądarki. To rozszerza zakres możliwości tworzenia interaktywnych aplikacji webowych bazujących na języku HTML w połączeniu z JavaScript i CSS3. Obecnie powstaje wiele aplikacji webowych, które korzystają z nowych elementów HTML5, takich jak canvas i video, a także takich mechanizmów jak Web storage.

Selenium WebDriver zawiera gotowe funkcjonalności testowania aplikacji webowych HTML5 dla niektórych przeglądarek. Do testowania tych funkcji możemy jednak również użyć języka JavaScript. W ten sposób można pracować ze wszystkimi przeglądarkami obsługiwanymi przez bibliotekę Selenium WebDriver.

W tym rozdziale skupię się na najważniejszych funkcjach HTML5 i pokażę, jak przetestować je za pomocą Selenium WebDriver.

Automatyzowanie odtwarzacza wideo HTML5

W HTML5 zdefiniowano nowy element <video> zapewniający standardowy sposób osadzania klipów wideo lub filmów na stronach webowych. Element <video> obsługują przeglądarki Internet Explorer w wersji 9. lub nowszej, Firefox, Opera, Chrome i Safari.

W tej recepturze pokażę, w jaki sposób można zautomatyzować testowanie elementu <video>. Omawiane mechanizmy udostępniają interfejs JavaScript z różnymi metodami i właściwościami przydatnymi do automatyzacji.

Jak to zrobić?

Utworzymy nowy test o nazwie testHTML5VideoPlayer do testowania elementu <video>. Do interakcji z elementem <video> skorzystamy z klasy JavascriptExecutor z biblioteki Selenium WebDriver. Będziemy kontrolować wideo z poziomu kodu testu, a także sprawdzać niektóre właściwości wideo. Oto przykład.

package com.secookbook.examples.chapter10;

import java.io.File;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.JavascriptExecutor;

import org.openqa.selenium.OutputType;

import org.openqa.selenium.TakesScreenshot;

import org.apache.commons.io.FileUtils;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import static org.junit.Assert.*;

public class HTML5VideoPlayer {

    private WebDriver driver;

    @Before

    public void setUp() {

        System.setProperty("webdriver.chrome.driver",

            "src/test/resources/drivers/chromedriver.exe");

    driver = new ChromeDriver();

    driver.get("http://cookbook.seleniumacademy.com/html5video.html");

    }

    @Test

    public void testHTML5VideoPlayer() throws Exception {

        // Pobranie elementu HTML5 video

        WebElement videoPlayer = driver.findElement(By.id("vplayer"));

        // Do interakcji z metodami i właściwościami elementu video, w celach automatyzacji,

        // będzie potrzebny obiekt JavascriptExecutor

        JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

        // Uzyskanie źródła klipu wideo, który będzie odtwarzany w odtwarzaczu

        String source = (String) jsExecutor.executeScript(

            "return arguments[0].currentSrc;", videoPlayer);

        // Obliczenie czasu trwania klipu wideo

        long duration = (Long) jsExecutor.executeScript(

            "return arguments[0].duration", videoPlayer);

        // Sprawdzenie, czy załadowano właściwy klip wideo oraz określenie czasu jego trwania

        assertEquals("http://html5demos.com/assets/dizzy.mp4",

source);

        assertEquals(25, duration);

        // Odtwarzanie wideo

        jsExecutor.executeScript("return arguments[0].play()",

videoPlayer);

        Thread.sleep(5000);

        // Pauza odtwarzania wideo

        jsExecutor.executeScript("arguments[0].pause()", videoPlayer);

        // Utworzenie zrzutu ekranu w celu późniejszej weryfikacji

        File scrFile = ((TakesScreenshot)

driver).getScreenshotAs(OutputType.FILE);

        FileUtils.copyFile(scrFile, new

File("target/screenshots/pause_play.png"));

    }

    @After

    public void tearDown() {

        driver.quit();

    }

}

Jak to działa?

Na początku zlokalizowaliśmy element <video>, tak aby można było wywoływać w JavaScript skojarzone z nim metody, a także by było możliwe pobieranie (ustawianie) jego właściwości. Element <video> możemy zlokalizować, podobnie jak inne elementy HTML za pomocą metody findElement(), w następujący sposób.

// Pobranie elementu HTML5 video

WebElement videoPlayer = driver.findElement(By.id("vplayer"));

Aby zweryfikować plik wideo wykorzystywany w odtwarzaczu wideo do odtwarzania, a także sprawdzić czas trwania klipu wideo, można odczytać właściwości currentSrc i duration. Właściwości te możemy pobrać, korzystając z dostępu do elementu <video> z kodu JavaScript. W tym celu utworzyliśmy egzemplarz klasy JavascriptExecutor w następujący sposób.

JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

Za pomocą metody executeScript() obiektu JavascriptExecutor frameworka Selenium WebDriver możemy uruchomić kod JavaScript w oknie przeglądarki. Wartość z kodu JavaScript możemy zwrócić poprzez przypisanie wartości do zmiennej. Wartość tę trzeba jednak odpowiednio zrzutować na podstawie typu zwracanej wartości. W takim przypadku właściwość currentSrc zwraca adres URL pliku wideo w postaci ciągu znaków.

String source = (String) jsExecutor.executeScript("return

arguments[0].currentSrc;", videoPlayer);

W poprzednim przykładzie odwołanie arguments[0] zostało zastąpione przez obiekt videoPlayer klasy WebElement za pomocą metody executeScript().

Odtwarzanie wideo

Jak wspominałem wcześniej, można również sterować odtwarzaniem wideo. Do tego celu służą metody elementu <video>, takie jak play() i pause(). Metody te możemy wywołać za pomocą metody executeScript() w następujący sposób.

// Odtwarzanie wideo

jsExecutor.executeScript("return arguments[0].play()",

videoPlayer);

Zobacz też

Receptura „Wykonywanie kodu JavaScript”, w rozdziale 4., „Korzystanie z Selenium API”.

Automatyzowanie interakcji z elementem HTML5 canvas

Wykorzystując nowy element HTML5 o nazwie canvas, programiści aplikacji webowych mogą teraz tworzyć atrakcyjne programy do rysowania w przeglądarkach internetowych. Element ten jest używany podczas programowania aplikacji do tworzenia rysunków i wykresów przy użyciu JavaScript. Klasa Canvas dostarcza kilka metod do rysowania ścieżek, ramek, okręgów, znaków oraz do dodawania obrazów.

W tej recepturze, korzystając z należącej do biblioteki Selenium WebDriver klasy action umożliwiającej poruszanie myszą, zautomatyzuję prostą aplikację do rysowania. Zaimplementuję również własność porównywania obrazów w celu przetestowania rysowania na elemencie canvas.

Element <canvas> obsługują przeglądarki Internet Explorer w wersji 9. lub nowszej, Firefox, Opera, Chrome i Safari.

Jak to zrobić?

W celu przetestowania elementu <canvas> utworzymy nowy test o nazwie testHTML5CanvasDrawing. Na elemencie <canvas> narysujemy kształt za pomocą sekwencji ruchów myszą. Zawartość elementu canvas porównamy z wcześniej przechwyconym obrazem i sprawdzimy, czy kształt został odrysowany w następujący sposób.

package com.secookbook.examples.chapter10;

import java.io.File;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.firefox.FirefoxDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.interactions.Actions;

import org.openqa.selenium.support.ui.Select;

import org.apache.commons.io.FileUtils;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import com.secookbook.examples.chapter09.CompareUtil;

import com.secookbook.examples.chapter09.WebElementExtender;

import static org.junit.Assert.*;

public class HTML5CanvasDrawing {

    private WebDriver driver;

    @Before

    public void setUp() {

        driver = new FirefoxDriver();

        driver.get("http://cookbook.seleniumacademy.com/html5canvasdraw.html");

    }

    @Test

    public void testHTML5CanvasDrawing() throws Exception {

        // Pobranie elementu HTML5 canvas

        WebElement canvas = driver.findElement(By.id("imageTemp"));

        // Wybranie narzędzia ołówka (pencil)

        Select drawTools = new

Select(driver.findElement(By.id("dtool")));

        drawTools.selectByValue("pencil");

        // Utworzenie łańcucha obiektów klasy Action w celu narysowania figury na elemencie canvas

        Actions builder = new Actions(driver);

        builder.clickAndHold(canvas).moveByOffset(10,

50).moveByOffset(50, 10)

            .moveByOffset(-10, -50).moveByOffset(-50, -10).release()

            .perform();

        // Uzyskanie zrzutu elementu canvas po wykonaniu rysunku i porównanie go

        // z wersją bazową

        FileUtils.copyFile(WebElementExtender.captureElementPicture(canvas

),

            new File("target/screenshots/drawing.png"));

        assertEquals(CompareUtil.Result.Matched,

CompareUtil.CompareImage(

            "src/test/resources/testdata/base_drawing.png",

"target/screenshots/drawing.png"));

    }

    @After

    public void tearDown() {

        driver.quit();

    }

}

Jak to działa?

Aby narysować na elemencie canvas, najpierw wybierzemy narzędzie do rysowania. W tym celu z rozwijanego menu Drawing Tool wybieramy pencil. Do pracy z rozwijanymi menu i listami Selenium WebDriver dostarcza klasę Select. W teście, w celu wybrania narzędzia pencil wywołano metodę selectByValue().

Select drawTools = new Select(driver.findElement(By.id("dtool")));

drawTools.selectByValue("pencil");

Następnie, posługując się generatorem akcji frameworka Selenium WebDriver, narysujemy na elemencie canvas figurę. Selenium WebDriver wykona sekwencję ruchów myszą przez wywołanie metody moveByOffset(). Podczas rysowania przycisk myszy jest wciśnięty. Jego zwolnienie następuje po zakończeniu rysowania.

builder.clickAndHold(canvas).moveByOffset(10, 50).

                 moveByOffset(50,10).

                 moveByOffset(-10,-50).

                 moveByOffset(-50,-10).release().perform();

Generator akcji frameworka Selenium WebDriver naśladuje operacje myszy. Dzięki temu można dokładnie odzwierciedlić działania użytkownika podczas rysowania kształtu na elemencie canvas.

Na koniec przechwycimy obraz elementu canvas, skorzystamy z metody captureElementBitmap() klasy WebElementExtender. Aby sprawdzić, czy operacja rysowania na elemencie canvas działa zgodnie z oczekiwaniami, obraz uzyskany ze zrzutu zostanie porównany z obrazem bazowym.

Zobacz też

Receptura „Wykonywanie kodu JavaScript”, w rozdziale 4., „Korzystanie z Selenium API”,

Receptura „Porównywanie obrazów w Selenium”, w rozdziale 9., „Rozszerzanie Selenium”.

Web storage — testowanie lokalnego magazynu

W HTML5 wprowadzono bezpieczniejszy i szybszy sposób przechowywania lokalnych danych aplikacji webowej w przeglądarce użytkownika. Wcześniej było to realizowane z wykorzystaniem plików cookie. W przypadku tego sposobu dane nie są dołączane do odpowiedzi na każde żądanie wysyłane na serwer, ale używane tylko wtedy, gdy użytkownik ich zażąda. Możliwe jest również przechowywanie dużych ilości danych bez wpływu na wydajność witryny. Dane są przechowywane w postaci par klucz-wartość, a aplikacja webowa ma dostęp tylko do tych danych, które sama zapisała.

HTML5 za pośrednictwem języka JavaScript udostępnia interfejs localStorage, który pozwala na magazynowanie danych bez określania daty ważności. Dane nie będą usunięte, gdy użytkownik zamknie przeglądarkę, i są dostępne przez cały czas. Aby obejrzeć te dane w Google Chrome, kliknij zakładkę Zbadaj/Resources.

Mechanizm Web storage obsługują przeglądarki Internet Explorer 8 +, Firefox, Opera, Chrome i Safari.

W tej recepturze sprawdzę, czy strona WWW przechowuje dane w lokalnym magazynie zgodnie z oczekiwaniami.

Jak to zrobić?

Utworzymy test, który sprawdzi, czy strona WWW utworzyła wpis w magazynie lokalnym oraz czy została tam zapisana wartość. W celu uzyskania dostępu do interfejsu localStorage użyjemy klasy Selenium WebDriver JavascriptExecutor.

package com.secookbook.examples.chapter10;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.JavascriptExecutor;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import static org.junit.Assert.*;

public class HTML5LocalStorage {

    private WebDriver driver;

    @Before

    public void setUp() {

        System.setProperty("webdriver.chrome.driver",

            "src/test/resources/drivers/chromedriver.exe");

        driver = new ChromeDriver();

        driver.get("http://cookbook.seleniumacademy.com/html5storage.html"

);

    }

    @Test

    public void testHTML5LocalStorage() throws Exception {

        JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

        // Pobranie bieżącej wartości localStorage.lastname. Powinna to być wartość Kowalski

        String lastName = (String) jsExecutor

            .executeScript("return localStorage.lastname;");

        assertEquals("Kowalski", lastName);

    }

    @After

    public void tearDown() {

        driver.quit();

    }

}

Jak to działa?

Podczas wykonywania tego testu Selenium WebDriver załaduje stronę, która sięgnie do lokalnego magazynu i utworzy nową parę klucz-wartość jako lastName = Kowalski. Możemy to sprawdzić, odczytując klucz lastName z interfejsu localStorage za pomocą metody executeScript() klasy JavascriptExecutor. Metoda ta zwróci wartość klucza lastName jako ciąg znaków (String).

String lastName = (String) jsExecutor.executeScript("return

localStorage.lastname;");

Co dalej?

Być może podczas testowania będzie trzeba bezpośrednio ustawić wartości klucza w lokalnym magazynie. Nową wartość możemy przypisać do istniejącego klucza bezpośrednio z poziomu JavaScript w następujący sposób.

// Ustawienie bieżącej wartości localStorage.lastname na Domański

jsExecutor.executeScript("localStorage.lastname = arguments[0];",

"Domański");

Zobacz też

Receptura „Czyszczenie magazynu lokalnego i magazynu sesji”.

Web storage — testowanie pamięci sesji

Podobny do magazynu lokalnego jest magazyn sesji, który przechowuje dane tylko dla wybranej sesji. Dane są usuwane, gdy użytkownik zamknie okno przeglądarki. Dostęp do magazynu sesji można uzyskać za pomocą interfejsu sessionStorage z poziomu JavaScript.

W tej recepturze sprawdzę, czy strona WWW zgodnie z oczekiwaniami przechowuje dane w magazynie sesji.

Jak to zrobić?

Utwórzmy przypadek testowy, w którym załadujemy stronę WWW implementującą licznik klikania przycisku i zapisującą w magazynie sesji jego wartość za każdym razem, gdy nastąpi kliknięcie. Następnie sprawdzimy, czy w magazynie sesji została zapisana nowa wartość licznika.

package com.secookbook.examples.chapter10;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.JavascriptExecutor;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import static org.junit.Assert.*;

public class HTML5SessionStorage {

    private WebDriver driver;

    @Before

    public void setUp() {

        System.setProperty("webdriver.chrome.driver",

            "src/test/resources/drivers/chromedriver.exe");

        driver = new ChromeDriver();

        driver.get("http://cookbook.seleniumacademy.com/html5storage.html"

);

        }

    @Test

    public void testHTML5SessionStorage() throws Exception {

        WebElement clickButton = driver.findElement(By.id("click"));

        WebElement clicksField = driver.findElement(By.id("clicks"));

        JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

        // Pobranie bieżącej wartości sessionStorage.clickCount. Powinna to być wartość null

        String clickCount = (String) jsExecutor

            .executeScript("return sessionStorage.clickCount;");

        assertEquals(null, clickCount);

        assertEquals("0", clicksField.getAttribute("value"));

        // Kliknięcie przycisku. To zwiększy wartość sessionStorage.clickCount o 1

        clickButton.click();

        // Pobranie bieżącej wartości sessionStorage.clickCount. Powinna to być wartość 1

        clickCount = (String) jsExecutor

            .executeScript("return sessionStorage.clickCount;");

        assertEquals("1", clickCount);

        assertEquals("1", clicksField.getAttribute("value"));

    }

    @After

    public void tearDown() {

        driver.quit();

    }

}

Jak to działa?

Podczas wykonywania tego testu, przy każdym kliknięciu przycisku przez Selenium WebDriver po raz pierwszy, w magazynie sesji tworzony jest nowy klucz. Dla kolejnych kliknięć ta wartość jest zwiększana o jeden.

Działanie magazynu sesji można zweryfikować, podobnie jak działanie magazynu lokalnego. Wystarczy odczytać klucz clickCount z interfejsu sessionStorage za pomocą metody executeScript() klasy JavascriptExecutor. Metoda zwraca wartość klucza clickCount jako ciąg znaków (String).

// Kliknięcie przycisku. To zwiększy wartość sessionStorage.clickCount

// o 1

clickButton.click();

// Pobranie bieżącej wartości sessionStorage.clickCount. Powinna to być wartość 1

clickCount = (String) jsExecutor.executeScript("return

    sessionStorage.clickCount;");

Po zamknięciu przeglądarki, gdy test się zakończy, klucz clickCount zostanie usunięty z magazynu sesji.

Zobacz też

Receptura „Czyszczenie magazynu lokalnego i magazynu sesji”.

Czyszczenie magazynu lokalnego i magazynu sesji

Podczas uruchamiania testów aplikacji HTML5 z wykorzystaniem magazynu lokalnego i magazynu sesji tworzy się w nich wiele wpisów. W przypadku uruchamiania paczki testów w środowisku, które było używane wcześniej, dobrym pomysłem jest wyczyszczenie obu magazynów przed rozpoczęciem testów.

W tej recepturze pokażę sposób usuwania elementów z magazynów lokalnego i sesji oraz czyszczenia zapisanych w nich wartości.

Jak to zrobić?

Aby usunąć określony element z magazynu lokalnego lub magazynu sesji, można użyć metody removeItem() w następujący sposób.

// Aby usunąć z lokalnego magazynu określony klucz razem z jego wartością, możemy użyć metody removeItem

JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

jsExecutor.executeScript("localStorage.removeItem(lastname);");

W przypadku magazynu sesji można skorzystać z następującego fragmentu kodu.

JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

jsExecutor.executeScript("sessionStorage.removeItem(lastname);");

Aby wyczyścić wszystkie elementy w magazynie lokalnym lub sesji, można użyć metody clear() w następujący sposób.

// Wyczyszczenie wartości zapamiętanych w magazynie lokalnym

JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

jsExecutor.executeScript("localStorage.clear();");

W przypadku magazynu sesji można skorzystać z następującego fragmentu kodu.

// Wyczyszczenie wartości zapamiętanych w magazynie sesji

JavascriptExecutor jsExecutor = (JavascriptExecutor) driver;

jsExecutor.executeScript("sessionStorage.clear();");

Zobacz też

Receptura „Web storage — testowanie pamięci sesji”,

Receptura „Web storage — testowanie lokalnego magazynu”.

Rozdział 11.
BDD — tworzenie oprogramowania sterowane zachowaniami

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Wykorzystanie technik BDD w Javie za pomocą biblioteki Cucumber-JVM i frameworka Selenium WebDriver.

Wykorzystanie technik BDD w środowisku .NET za pomocą biblioteki SpecFlow.NET i frameworka Selenium WebDriver.

Wykorzystanie biblioteki Capybara, notacji Cucumber i frameworka Selenium WebDriver w Ruby.

Wykorzystanie biblioteki Behave i frameworka Selenium WebDriver w Pythonie.

Wprowadzenie

Wytwarzanie oprogramowania sterowanego zachowaniem (ang. Behavior Driven Development — BDD) to technika zwinnego wytwarzania oprogramowania, która zwiększa rolę paradygmatu wytwarzania oprogramowania sterowanego testami (ang. Test Driven Development — TDD) i testów akceptacyjnych oraz zachęca do współpracy pomiędzy programistami, inżynierami QA, ekspertami dziedzinowymi oraz innymi interesariuszami. Początek technice BDD dał Dan North w 2003 roku po opublikowaniu słynnego artykułu Introducing BDD. Wspomniany artykuł jest dostępny pod adresem http://dannorth.net/introducing-bdd/.

BDD koncentruje się na uzyskaniu jasnego zrozumienia pożądanych zachowań aplikacji w wyniku rozmów z zainteresowanymi stronami prowadzonych z wykorzystaniem wszechobecnego języka, zgodnie z opisem dostępnym pod adresem http://behaviour-driven.org/.

BDD rozszerza TDD dzięki pisaniu przypadków testowych w języku naturalnym, czytelnym również dla osób niebędących programistami. Użytkownicy opisują cechy funkcjonalne i scenariusze do ich testowania w plikach tekstowych z wykorzystaniem języka Gherkin oraz struktur Given, When i Then. Więcej informacji na temat języka Gherkin można znaleźć pod adresami http://en.wikipedia.org/wiki/Behavior-driven_development oraz https://github.com/cucumber/cucumber/wiki/Gherkin.

Struktury Given, When i Then języka Gherkin można opisać w następujący sposób.

Given: reprezentuje początkowy kontekst (warunek konieczny).

When: reprezentuje użytkownika wykonującego działania (aktor + akcja).

Then: gwarantuje jakiś wynik (rezultat możliwy do zaobserwowania).

W BDD proces rozpoczyna się od użytkowników systemu oraz zespołu programistów, którzy omawiają cechy funkcjonalne, historyjki użytkownika i scenariusze. Są one dokumentowane w plikach cech funkcjonalnych lub plikach historyjek pisanych w języku Gherkin. Następnie programiści, korzystając z frameworka BDD oraz cyklu red-green-refactor (czerwony-zielony-refaktoryzacja), uruchamiają te cechy funkcjonalne. W tym celu piszą pliki definicji kroków, w których mapują kroki opisane w scenariuszach na kod automatyzacji i uruchamiają je dopóki, dopóty nie zostaną spełnione wszystkie kryteria akceptacji.

Techniki BDD są również znane jako wytwarzanie oprogramowania sterowane testami akceptacyjnymi (ang. acceptance test driven development — ATDD) lub testowanie historyjek (ang. story testing).

W tym rozdziale omówię sposób realizacji technik BDD za pomocą takich frameworków jak Cucumber-JVM, SpecFlow.NET, Capybara oraz Behave używanych razem z Selenium WebDriver.

Wykorzystanie technik BDD w Javie za pomocą biblioteki Cucumber-JVM i frameworka Selenium WebDriver

BDD/ATDD zyskuje popularność wśród członków społeczności Agile, a Cucumber-JVM jest głównym narzędziem używanym do implementacji tej praktyki w Javie. Cucumber-JVM jest portem dla języka Java frameworka Cucumber powszechnie stosowanego w środowisku Ruby.

Cucumber-JVM umożliwia programistom, inżynierom QA oraz interesariuszom nietechnicznym, czyli przedstawicielom biznesu, pisanie scenariuszy i opisywanie cech funkcjonalnych w plikach tekstowych, przy użyciu języka Gherkin. Przy tworzeniu opisów stosowane są minimalne ograniczenia gramatyczne — z zastosowaniem struktur Given, When i Then.

Plik opisu cech funkcjonalnych jest następnie wspierany plikiem definicji kroku. Znajduje się w nim implementacja automatycznych kroków uruchamiania scenariuszy zapisanych w plikach opisu cech funkcjonalnych. Oprócz API testowych bazujących na Cucumber-JVM — dzięki połączeniu z frameworkiem Selenium WebDriver — można również stosować API testów na poziomie interfejsu użytkownika.

W tej recepturze wykorzystam Cucumber-JVM, narzędzie Maven i Selenium WebDriver do zaimplementowania testów funkcjonalności przelewu funduszy w aplikacji bankowości online.

Przygotuj się

W środowisku Eclipse utwórz nowy projekt Maven o nazwie FundTransfer.

Dodaj do pliku POM.XML następujące zależności.

<project xmlns="http://maven.apache.org/POM/4.0.0"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0

http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <modelVersion>4.0.0</modelVersion>

    <groupId>FundTransfer</groupId>

    <artifactId>FundTransfer</artifactId>

    <version>0.0.1-SNAPSHOT</version>

    <dependencies>

        <dependency>

            <groupId>info.cukes</groupId>

            <artifactId>cucumber-java</artifactId>

            <version>1.2.4</version>

            <scope>test</scope>

        </dependency>

        <dependency>

            <groupId>info.cukes</groupId>

            <artifactId>cucumber-junit</artifactId>

            <version>1.2.4</version>

            <scope>test</scope>

        </dependency>

        <dependency>

            <groupId>junit</groupId>

            <artifactId>junit</artifactId>

            <version>4.12</version>

            <scope>test</scope>

        </dependency>

        <dependency>

            <groupId>org.seleniumhq.selenium</groupId>

            <artifactId>selenium-java</artifactId>

            <version>2.47.1</version>

        </dependency>

    </dependencies>

</project>

Jak to zrobić?

Aby utworzyć testy BDD/ATDD z wykorzystaniem biblioteki Cucumber-JVM, wykonaj następujące czynności.

W oknie Package Explorer w środowisku Eclipse wybierz projekt FundTransfer. W oknie Package Explorer zaznacz i kliknij prawym przyciskiem myszy src/test/resources. Aby dodać nowy pakiet, wybierz z menu polecenie New/Package, tak jak pokazano na poniższym zrzucie ekranu.

W polu tekstowym Name: wpisz fundtransfer.test, a następnie kliknij przycisk Finish.

Dodaj do pakietu nowy plik. Nadaj plikowi nazwę fundtransfer.feature, tak jak pokazano na poniższym zrzucie ekranu.

Dodaj do tego pliku cechę funkcjonalną Przelew środków i scenariusze.

# language:pl

    Funkcja: Przelew środków klienta

        Jako klient

        chcę przelać środki

        tak, aby można było wysyłać pieniądze do moich przyjaciół i rodziny

    Scenariusz: Prawidłowy odbiorca płatności

        Zakładając użytkownik jest na stronie Przelew środków

        Gdy wprowadzi "Jerzy" jako nazwę odbiorcy płatności

        I wprowadzi "100" jako kwota

        I prześle żądanie Przelew środków

        Wtedy upewnij się że przelew środków zakończy się pomyślnie komunikatem "100 PLN pomyślnie przelano do Jerzy"

    Szablon scenariusza: Nieprawidłowe warunki

        Zakładając użytkownik jest na stronie Przelew środków

        Gdy wprowadzi "<odbiorca>" jako nazwę odbiorcy płatności

        I wprowadzi "<kwota>" jako kwotę przelewu

        I prześle żądanie Przelew środków

        Wtedy upewnij się że zostanie wyświetlony komunikat o niepowodzeniu transakcji:"<komunikat>"

    Przykłady:

        | odbiorca | kwota | komunikat |

        | Unmesh | 100 | Przelew nieudany!! 'Unmesh' nie jest zarejestrowany na twojej liście odbiorców |

        | Tomasz| 100000 | Przelew nieudany! Nie można przekroczyć salda |

W oknie Package Explorer zaznacz src/test/java i kliknij prawym przyciskiem myszy. Aby dodać nowy pakiet, wybierz z menu polecenie New/Package, tak jak pokazano na poniższym zrzucie ekranu.

W nowo powstałym pakiecie utwórz klasę o nazwie FundTransferStepDefs. Dodaj do tej klasy następujący kod.

package fundtransfer.test;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import cucumber.api.java.*;

import cucumber.api.java.en.*;

import static org.junit.Assert.assertEquals;

public class FundTransferStepDefs {

    protected WebDriver driver;

    @Before

    public void setUp() {

        System.setProperty("webdriver.chrome.driver", "c:\\chromedriver\\chromedriver.exe");

        driver = new ChromeDriver();

    }

    @Given("użytkownik jest na stronie Przelew środków")

    public void theUserIsOnFundTransferPage() {

        driver.get("http://localhost/fundtransfer/fundTransfer.html");

    }

    @When("wprowadzi \"([^\"]*)\" jako nazwę odbiorcy płatności")

    public void heEntersPayeeName(String payeeName) {

        driver.findElement(By.id("payee")).sendKeys(payeeName);

    }

    @When("^wprowadzi \"([^\"]*)\" jako kwotę przelewu$")

    public void heEntersAmount(String amount){

        driver.findElement(By.id("amount")).sendKeys(amount);

    }

    @And("prześle żądanie Przelew środków")

    public void heSubmitsRequestForFundTransfer() {

        driver.findElement(By.id("transfer")).click();

    }

    @Then("^upewnij się że przelew środków zakończy się pomyślnie komunikatem \"([^\"]*)\"$")

    public void ensureTheFundTransferIsComplete(String msg) {

        WebElement message = driver.findElement(By.id("message"));

        assertEquals(msg, message.getText());

    }

    @Then("^upewnij się że zostanie wyświetlony komunikat o niepowodzeniu transakcji:\"([^\"]*)\"$")

    public void ensureATransactionFailureMessage(String msg)

    {

        WebElement message = driver.findElement(By.id("message"));

        assertEquals(msg, message.getText());

    }

    @After

    public void tearDown() {

        driver.close();

    }

}

Utwórz klasę pomocniczą RunCukesTest, w której będą zdefiniowane konfiguracje Cucumber-JVM.

package fundtransfer.test;

import cucumber.api.CucumberOptions;

import cucumber.api.junit.Cucumber;

import org.junit.runner.RunWith;

@RunWith(Cucumber.class)

@CucumberOptions(plugin = {"pretty",

        "html:target/report/report.html",

        "json:target/report/cucu_json_report.json",

        "junit:target/report/cucumber_junit_report.xml"})

public class RunCukesTest {

}

Aby uruchomić testy za pomocą środowiska Maven, wybierz projekt FundTransfer w oknie Package Explorer. Kliknij prawym przyciskiem myszy nazwę projektu i wybierz polecenie Run As/Maven test. Maven uruchomi wszystkie testy w projekcie.

Po zakończeniu testu zostanie wygenerowany raport w formacie HTML podobny do tego, który pokazano na poniższym zrzucie ekranu. Aby wyświetlić ten raport, otwórz stronę index.html w folderze target\report.

Jak to działa?

Tworzenie testów w środowisku Cucumber-JVM obejmuje trzy główne kroki: napisanie pliku opisu cechy funkcjonalnej, implementację zautomatyzowanych kroków z wykorzystaniem pliku definicji kroku oraz utworzenie kodu pomocniczego według potrzeb.

W środowisku Cucumber-JVM do pisania cech funkcjonalnych wykorzystywana jest składnia Gherkin. Plik opisu cechy funkcjonalnej definiuje cechę funkcjonalną, a następnie scenariusze testowania tej cechy funkcjonalnej.

Funkcja: Przelew środków klienta

        Jako klient

        chcę przelać środki tak aby można było wysyłać pieniądze do moich przyjaciół i rodziny

W celu przetestowania cechy funkcjonalnej opisanej w pliku opisu funkcji można napisać dowolną liczbę scenariuszy. Sekcja Scenariusz zawiera nazwę scenariusza oraz kroki potrzebne do zdefiniowania tego scenariusza, a także dane testowe wymagane do uruchomienia tego scenariusza za pomocą aplikacji.

Scenariusz: Prawidłowy odbiorca płatności

        Biorąc pod uwagę użytkownik jest na stronie Przelew środków

        Gdy wprowadzi "Jerzy" jako nazwę odbiorcy płatności

        I wprowadzi "100" jako kwotę

        I prześle żądanie Przelew środków

        Wtedy upewnij się że przelew środków zakończy się pomyślnie "100 PLN pomyślnie przelano to Jerzy!"

Członkowie zespołu wykorzystują pliki opisu cech funkcjonalnych do budowania i walidowania systemu. Takie frameworki jak Cucumber zapewniają możliwość automatycznej weryfikacji poprawności cech funkcjonalnych, bo pozwalają na implementację zautomatyzowanych kroków. W tym celu trzeba utworzyć plik definicji kroku, który zmapuje kroki z pliku definicji cechy funkcjonalnej na kod automatyzacji. Pliki definicji kroków implementują metodę kroku za pomocą specjalnej adnotacji. Przykładowo w poniższym kodzie pliku definicji kroku w celu zmapowania kroku "Gdy wprowadzi "Jerzy" jako nazwę odbiorcy płatności" z pliku opisu cechy funkcjonalnej wykorzystano adnotację @When. Gdy framework podejmie próbę uruchomienia tego kroku, zostanie wywołana metoda heEntersPayeeName() i za pośrednictwem wyrażenia regularnego zostaną do niej przekazane dane wyodrębnione z pliku opisu cechy funkcjonalnej.

@When("wprowadzi \"([^\"]*)\" jako nazwę odbiorcy płatności")

    public void heEntersPayeeName(String payeeName) {

        driver.findElement(By.id("payee")).sendKeys(payeeName);

}

W tej metodzie znalazł się kod WebDriver, którego zadaniem jest zlokalizowanie pola tekstowego payeeName i wprowadzenie do niego nazwy odbiorcy płatności przy użyciu metody sendKeys().

Plik definicji kroku spełnia rolę szablonu dla wszystkich kroków z pliku opisu cechy funkcjonalnej, natomiast w scenariuszach kroki są łączone i dopasowywane na podstawie warunków testu.

Klasa pomocnicza RunCukesTest dostarcza konfigurację Cucumber-JVM — na przykład opisuje sposób uruchamiania cech funkcjonalnych oraz kroków za pomocą frameworka JUnit, wskazuje narzędzia formatowania raportów oraz lokalizacje plików.

@RunWith(Cucumber.class)

@CucumberOptions(plugin = {"pretty",

"html:target/report/report.html",

    "json:target/report/cucu_json_report.json",

    "junit:target/report/cucumber_junit_report.xml"})

public class RunCukesTest {

}

Co dalej?

W tym przykładzie metody definicji kroków bezpośrednio wywołują metody frameworka Selenium WebDriver. Za pomocą obiektu Page można jednak utworzyć warstwę abstrakcji — zdefiniować odrębną klasę zawierającą definicje wszystkich elementów z klasy FundTransferPage.

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.support.CacheLookup;

import org.openqa.selenium.support.FindBy;

import org.openqa.selenium.support.PageFactory;

public class FundTransferPage {

    @FindBy(id = "payee")

    @CacheLookup

    public WebElement payeeField;

    @FindBy(id = "amount")

    public WebElement amountField;

    @FindBy(id = "transfer")

    public WebElement transferButton;

    @FindBy(id = "message")

    public WebElement messageLabel;

    public FundTransferPage(WebDriver driver)

    {

        if(!"Online Fund Transfers".equals(driver.getTitle()))

            throw new IllegalStateException("To nie jest strona

                       Przelew środków");

        PageFactory.initElements(driver, this);

    }

}

Zobacz też

Receptura „Konfigurowanie środowiska tworzenia testów Selenium WebDriver dla języka Java z Eclipse i Maven”, w rozdziale 1., „Od czego zacząć?”,

Receptura „Korzystanie z klasy PageFactory do udostępniania elementów na stronie”, w rozdziale 8. „Korzystanie ze wzorca Page Object”.

Wykorzystanie technik BDD w środowisku .NET za pomocą biblioteki SpecFlow.NET i frameworka Selenium WebDriver

W poprzedniej recepturze zaprezentowałem sposób wykorzystania frameworka Selenium WebDriver z Cucumber-JVM w celu użycia technik BDD (ATDD). W tej recepturze spróbuję użyć podobnej kombinacji w środowisku .NET. Techniki BDD na platformie .NET można zaimplementować, korzystając z biblioteki SpecFlow.NET oraz dowiązań .NET do frameworka Selenium WebDriver.

Inspiracją dla biblioteki SpecFlow.NET jest biblioteka Cucumber. Specyfikacje są pisane za pomocą tego samego języka Gherkin. W tej recepturze skorzystam z biblioteki SpecFlow.NET w celu zaimplementowania testów cechy funkcjonalnej Przelew środków. Użyję także obiektów Page w celu utworzenia reprezentacji strony FundTransferPage.

Przygotuj się

Receptura powstała z wykorzystaniem biblioteki SpecFlow.NET wersji 1.9.0 oraz środowiska Microsoft Visual Studio Professional 2013. W ramach przygotowań wykonaj następujące czynności.

Pobierz i zainstaluj bibliotekę SpecFlow z galerii Visual Studio dostępnej pod adresem http://visualstudiogallery.msdn.microsoft.com/9915524d-7fb0-43c3-bb3c-a8a14fbd40ee.

Korzystając z adresu http://visualstudiogallery.msdn.microsoft.com/9915524d-7fb0-43c3-bb3c-a8a14fbd40ee, pobierz NUnit Test Adapter i zainstaluj go.

Spowoduje to zainstalowanie szablonu projektu oraz innych plików potrzebnych do obsługi SpecFlow.NET w środowisku Visual Studio 2013.

Jak to zrobić?

Aby przetestować cechę funkcjonalną przelewu środków za pomocą biblioteki SpecFlow.NET, wykonaj następujące czynności.

Uruchom środowisko Microsoft Visual Studio.

Utwórz nowy projekt w programie Visual Studio poprzez wybranie polecenia File/New/Project. Wybierz projekt typu Visual C# Class Library. Nadaj projektowi nazwę FundTransfer.specs, tak jak pokazano na poniższym zrzucie ekranu.

Korzystając z menedżera pakietów NuGet, dodaj biblioteki SpecFlow.NET, WebDriver i NUnit. W oknie Solution Explorer kliknij prawym przyciskiem myszy solucję FundTransfer.specs i wybierz polecenie Manage NuGet Packages…, tak jak pokazano na pierwszym zrzucie ekranu poniżej.

W oknie dialogowym FundTransfer.specs — Manage NuGet Packages wybierz Online i poszukaj pakietów SpecFlow. W wyniku wyszukiwania wyświetlą się następujące sugestie. Zobacz drugi zrzut poniżej.

Wybierz z listy pozycję SpecFlow.Nunit, a następnie kliknij przycisk Install. NuGet pobierze i zainstaluje w solucji pakiet SpecFlow.NUnit oraz inne pakiety zależne. Operacja ta zajmuje kilka minut.

W oknie dialogowym FundTransfer.specs — Manage NuGet Packages poszukaj pakietu WebDriver.

Wybierz na liście Selenium WebDriver oraz Selenium WebDriver Support Classes, a następnie kliknij przycisk Install.

Zamknij okno dialogowe FundTransfer.specs — Manage NuGet Packages.

Tworzenie pliku specyfikacji

Aby utworzyć plik specyfikacji, wykonaj następujące czynności.

W oknie Solution Explorer kliknij prawym przyciskiem myszy solucję FundTransfer.specs. Wybierz polecenie Add/New Item.

W oknie dialogowym Add New Item — FundTransfer.specs wybierz pozycję SpecFlow Feature File i w polu tekstowym Name: wpisz FundTransfer.feature. Kliknij przycisk Add, tak jak pokazano na poniższym zrzucie ekranu.

W oknie Editor wyświetli się zakładka FundTransfer.feature.

Domyślnie biblioteka SpecFlow dodaje w pliku opisu cech funkcjonalnych cechę-atrapę. Zastąp zawartość tego pliku następującą cechą funkcjonalną i scenariuszami.

# language:pl

Właściwość: Przelew środków klienta

        Jako klient

        chcę przelać środki

        tak, aby można było wysyłać pieniądze do moich przyjaciół i rodziny

Scenariusz: Prawidłowy odbiorca płatności

        Zakładając użytkownik jest na stronie Przelew środków

        Jeśli wprowadzi "Jerzy" jako nazwę odbiorcy płatności

        I wprowadzi "100" jako kwotę

        I prześle żądanie Przelew środków

        Wtedy upewnij się, że przelew środków zakończy się pomyślnie komunikatem "100 PLN pomyślnie przelano do Jerzy!!"

Scenariusz: Nieprawidłowy odbiorca płatności

        Zakładając użytkownik jest na stronie Przelew środków

        Jeśli wprowadzi "Jacek" jako nazwę odbiorcy płatności

        I wprowadzi "100" jako kwotę

        I prześle żądanie Przelew środków

        Wtedy upewnij się, że transakcja zakończyła się niepowodzeniem z komunikatem "Przelew nieudany!! 'Jacek' nie jest zarejestrowany na twojej liście odbiorców"

Scenariusz: Próba przelewu kwoty przekraczającej dopuszczalny debet

        Zakładając użytkownik jest na stronie Przelew środków

        Jeśli wprowadzi "Tomasz" jako nazwę odbiorcy płatności

        I wprowadzi "1000000" jako kwotę przekraczającą limit

        I prześle żądanie Przelew środków

        Wtedy upewnij się, że transakcja zakończyła się niepowodzeniem z komunikatem "Przelew nieudany!! Nie można przekroczyć salda"

Tworzenie pliku definicji kroku

Aby utworzyć plik definicji kroku, wykonaj następujące czynności.

Aby dodać plik definicji kroku, kliknij w oknie Solution Explorer prawym przyciskiem myszy solucję FundTransfer.specs. Wybierz polecenie Add/New Item.

W oknie dialogowym Add New Item — FundTransfer.specs wybierz SpecFlow Step Definition File, a następnie w polu tekstowym Name: wpisz FundTransferStepDefs.cs.

Kliknij przycisk Add. Zostanie dodana nowa klasa C# zawierająca kroki-atrapy. Zastąp zawartość tego pliku następujący kodem.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using TechTalk.SpecFlow;

using NUnit.Framework;

using OpenQA.Selenium;

namespace FundTransfer.specs

{

    [Binding]

    public class FundTransferStepDefs

    {

        FundTransferPage _ftPage = new

        FundTransferPage(Environment.Driver);

        [Given(@"użytkownik jest na stronie Przelew środków")]

        public void GivenUserIsOnFundTransferPage()

        {

            Environment.Driver.Navigate().GoToUrl("http:// localhost/fundtransfer/fundTransfer.html");

        }

        [When(@"wprowadzi ""(.*)"" jako nazwę odbiorcy płatności")]

        public void WhenUserEneteredIntoThePayeeNameField(string  payeeName)

        {

            _ftPage.payeeNameField.SendKeys(payeeName);

        }

        [When(@"wprowadzi ""(.*)"" jako kwotę")]

        public void WhenUserEneteredIntoTheAmountField(string  amount)

        {

            _ftPage.amountField.SendKeys(amount);

        }

        [When(@"wprowadzi ""(.*)"" jako kwotę przekraczającą limit")]

        public void WhenUserEneteredIntoTheAmountFieldAboveLimit

        (string amount)

        {

            _ftPage.amountField.SendKeys(amount);

        }

        [When(@"prześle żądanie Przelew środków")]

        public void WhenUserPressTransferButton()

        {

            _ftPage.transferButton.Click();

        }

        [Then(@"upewnij się, że przelew środków zakończy się pomyślnie komunikatem ""(.*)""")]

        public void ThenFundTransferIsComplete(string

        message)

        {

            Assert.AreEqual(message,

            _ftPage.messageLabel.Text);

        }

        [Then(@"upewnij się, że transakcja zakończyła się niepowodzeniem z komunikatem ""(.*)""")]

        public void ThenFundTransferIsFailed(string

        message)

        {

            Assert.AreEqual(message, _ftPage.messageLabel.Text);

        }

    }

}

Definiowanie obiektu Page i klasy pomocniczej

Aby zdefiniować obiekt Page i klasę pomocniczą, wykonaj następujące czynności.

Zdefiniuj obiekt Page dla strony Przelew środków poprzez dodanie nowego pliku klasy C#. Nadaj tej klasie nazwę FundTransferPage. Skopiuj do tej klasy następujący kod.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using OpenQA.Selenium;

using OpenQA.Selenium.Support.PageObjects;

namespace FundTransfer.specs

{

    class FundTransferPage

    {

        public FundTransferPage(IWebDriver driver)

        {

            PageFactory.InitElements(driver, this);

        }

        [FindsBy(How = How.Id, Using = "payee")]

        public IWebElement payeeNameField { get; set; }

        [FindsBy(How = How.Id, Using = "amount")]

        public IWebElement amountField { get; set; }

        [FindsBy(How = How.Id, Using = "transfer")]

        public IWebElement transferButton { get; set; }

        [FindsBy(How = How.Id, Using = "message")]

        public IWebElement messageLabel { get; set; }

    }

}

Potrzebna jest też klasa pomocnicza, która dostarczy egzemplarz WebDriver, a na koniec po sobie „posprząta”. Nadaj tej klasie nazwę Environment i skopiuj do niej poniższy kod.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using OpenQA.Selenium;

using OpenQA.Selenium.Chrome;

using TechTalk.SpecFlow;

namespace FundTransfer.specs

{

    [Binding]

    public class Environment

    {

        private static ChromeDriver driver;

        public static IWebDriver Driver

        {

            get { return driver ?? (driver = new

            ChromeDriver(@"C:\ChromeDriver")); }

        }

        [AfterTestRun]

        public static void AfterTestRun()

        {

            Driver.Close();

            Driver.Quit();

            driver = null;

        }

    }

}

Zbuduj solucję.

Uruchamianie testów

Aby uruchomić testy, wykonaj następujące czynności.

Otwórz okno Test Explorer przez kliknięcie opcji Test Explorer z podmenu Test/Windows należącego do menu głównego.

Wyświetlą się trzy scenariusze wyszczególnione w pliku opisu cechy funkcjonalnej, tak jak pokazano na poniższym zrzucie ekranu.

Aby przetestować wybraną cechę funkcjonalną, kliknij Run All. Wyświetlą się wyniki testów, tak jak pokazano na zrzucie ekranu poniżej.

Jak to działa?

Aby testować cechy funkcjonalne za pomocą SpecFlow.NET, najpierw trzeba zdefiniować pliki opisu cech funkcjonalnych. Opis cech funkcjonalnych dla SpecFlow.NET tworzy się za pomocą języka Gherkin.

Dla każdego kroku wymienionego w pliku opisu cechy funkcjonalnej należy utworzyć metodę w pliku definicji kroku. Do tego celu służą atrybuty Given, When i Then. Metody w pliku definicji kroków mogą również pobierać argumenty. Oto przykład wprowadzenia nazwy odbiorcy płatności.

[When(@"wprowadzi ""(.*)"" jako nazwę odbiorcy płatności")]

        public void WhenUserEneteredIntoThePayeeNameField(string  payeeName)

        {

            _ftPage.payeeNameField.SendKeys(payeeName);

        }

W tym przykładzie zautomatyzowaliśmy krok "Gdy wprowadzi "Jerzy" jako nazwę odbiorcy płatności". Skorzystaliśmy z atrybutu When i utworzyliśmy metodę WhenUserEneteredIntoThePayeeNameField. Metoda ta potrzebuje wartości nazwy odbiorcy płatności, która jest osadzona wewnątrz kroku. Nazwę tę biblioteka SpecFlow.NET wyodrębnia za pomocą wyrażenia regularnego. Wewnątrz metody posługujemy się egzemplarzem klasy FundTransferPage i wywołujemy metodę SendKeys()zmiennej członkowskiej payeeNameField, przekazując nazwę odbiorcy wyodrębnioną z opisu kroku. Skorzystanie z obiektu Page pomaga w wyodrębnieniu szczegółów lokalizatora i szczegółów strony z plików definicji kroków. Dzięki temu są one bardziej zrozumiałe i łatwiejsze do utrzymania.

Podczas budowania projektu SpecFlow.NET automatycznie generuje kod testu NUnit. Testy te są uruchamiane za pomocą eksploratora testów Visual Studio i pakietu NUnit Test Adaptor for Visual Studio. W ten sposób następuje walidacja cech funkcjonalnych.

Zobacz też

Receptura „Konfigurowanie środowiska tworzenia testów Selenium WebDriver dla języka Java z Eclipse i Maven”, w rozdziale 1., „Od czego zacząć?”.

Wykorzystanie biblioteki Capybara, Cucumber i frameworka Selenium WebDriver w Ruby

Capybara to framework testów akceptacyjnych dla aplikacji webowych napisanych w Ruby. Integruje się z frameworkami BDD napisanymi w Ruby, takimi jak Cucumber i RSpec, a także z Selenium WebDriver, dzięki czemu możliwe jest testowanie aplikacji webowych. Framework Capybara jest powszechnie wykorzystywany do testowania aplikacji Rails.

W tej recepturze pokażę, w jaki sposób skorzystać z frameworków Capybara, Cucumber i Selenium do testowania aplikacji Kalkulator BMI.

Przygotuj się

Zainstaluj gem Capybara za pomocą następującego polecenia.

gem install capybara

Dodatkowo w świeżej instalacji środowiska Ruby musisz również zainstalować gemy Cucumber i RSpec w następujący sposób.

gem install cucumber

gem install rspec

Jak to zrobić?

Najpierw należy utworzyć pliki opisu cech funkcjonalnych dla testowanych historyjek użytkownika. Historyjki są pisane w języku Gherkin, w formacie Cucumber, z wykorzystaniem struktur Given, When i Then. Aby utworzyć plik opisu cechy funkcjonalnej oraz plik definicji kroku zrozumiały dla frameworka Capybara, wykonaj następujące czynności.

Utwórz plik tekstowy BmiCalculate.feature o następującej treści.

Funkcja: Kalkulator BMI ma funkcję Oblicz

Scenariusz: Obliczanie BMI

    Zakładając jestem na stronie Kalkulator BMI

    Gdy wypełnię następujące pola:

    | heightCMS | 181 |

    | weightKg | 80 |

    Gdy kliknę "Oblicz"

    Wtedy powinienem otrzymać następujące wyniki:

    | bmi | 24.4 |

    | bmi_category | Waga normalna |

Następnie dla utworzonego wcześniej pliku opisu cechy funkcjonalnej trzeba opracować plik definicji kroku. Ten plik mapuje każdy krok z pliku opisu cechy funkcjonalnej na funkcję Capybara komunikującą się z interfejsem użytkownika. Utwórz plik Ruby BmiCalculate.rb zawierający następujący kod.

Given /^jestem na stronie Kalkulator BMI$/ do

    visit "http://localhost/bmicalculator/bmicalculator.html"

end

When /^wypełnię następujące pola:$/ do |table|

    table.rows_hash.each {|field, value| fill_in field,

        :with => value }

end

When /^kliknę "([^"]*)"$/ do |button|

    click_button(button)

end

Then /^powinienem otrzymać następujące wyniki:$/ do |table|

    table.rows_hash.each {|field, value| expect(find_field(field). value).to eq (value)}

end

Na koniec trzeba utworzyć plik konfiguracyjny, w którym będą zamieszczone parametry wymagane do uruchomienia cech funkcjonalnych zdefiniowanych w notacji Cucumber. Utwórz plik env.rb zawierający następujący kod.

require 'capybara'

require 'capybara/cucumber'

require 'selenium/webdriver'

Capybara.default_driver = :selenium

Capybara.register_driver :selenium do |app|

    Capybara::Selenium::Driver.new(app, :browser => :firefox)

end

Jak to działa?

Należy skopiować wszystkie opisane powyżej pliki do katalogu o nazwie features, a następnie użyć polecenia cucumber w następujący sposób.

cucumber

Ponieważ korzystamy z gemu Cucumber razem z Capybara, najpierw potrzebujemy pliku opisu cechy funkcjonalnej napisanej w prostym języku naturalnym. W poniższym przykładzie mamy krok, który wypełnia pola height i weight w aplikacji Kalkulator BMI.

Gdy wypełnię następujące pola:

    | heightCMS | 181 |

    | weightKg | 80 |

Kroki z pliku opisu cechy funkcjonalnej są następnie mapowane na komendy Capybara przy użyciu pliku definicji kroku napisanego w Ruby. W poniższym przykładzie wspomniany wcześniej format tabeli jest mapowany na polecenie Capybara fill_in.

When /^wypełnię następujące pola:$/ do |table|

    table.rows_hash.each {|field, value| fill_in field, :with =>

value }

end

Aby uruchomić te funkcje w środowisku Cucumber, potrzebujemy pliku konfiguracyjnego, który poinformuje framework Capybara, że w roli sterownika będzie wykorzystywana biblioteka Selenium.

require 'capybara'

require 'capybara/cucumber'

require 'selenium/webdriver'

Capybara.default_driver = :selenium

Capybara.register_driver :selenium do |app|

    Capybara::Selenium::Driver.new(app, :browser => :firefox)

end

Gdy Cucumber uruchomi cechy funkcjonalne, zostanie wygenerowany domyślny raport w następującym formacie.

Zobacz też

Receptura „Wykorzystanie technik BDD w Javie za pomocą biblioteki Cucumber-JVM i frameworka Selenium WebDriver”,

Receptura „Wykorzystanie technik BDD w środowisku .NET za pomocą biblioteki SpecFlow.NET i frameworka Selenium WebDriver”.

Receptura „Wykorzystanie biblioteki Behave i frameworka Selenium WebDriver w Pythonie”.

Wykorzystanie biblioteki Behave i frameworka Selenium WebDriver w Pythonie

Behave to framework BDD dostępny w języku Python. Zapewnia on mechanizmy bardzo podobne do tych, które oferuje biblioteka Cucumber.

W tej recepturze pokażę, w jaki sposób użyć frameworków Behave i Selenium WebDriver do testowania aplikacji przelewu środków.

Przygotuj się

Należy zainstalować Behave za pomocą następującego polecenia.

pip install behave

Jak to zrobić?

Behave wymaga utworzenia plików opisu cech funkcjonalnych reprezentujących testowane historyjki użytkownika. Historyjki te są pisane w języku Gherkin, w formacie Cucumber, z wykorzystaniem struktur Given, When i Then. Aby utworzyć plik opisu cechy funkcjonalnej oraz plik definicji kroku w Behave, wykonaj następujące czynności.

Utwórz folder o nazwie fundtransfer, a następnie otwórz w nim dwa podfoldery features i steps.

W folderze features utwórz plik tekstowy fundtransfer.feature z następującą zawartością.

Feature: Przelew srodkow klienta

        Jako klient,

        chce przelac srodki

        tak, aby mozna bylo wysylac pieniadze do moich przyjaciol i rodziny

Scenario: Prawidlowy odbiorca platnosci

        Given uzytkownik jest na stronie Przelew srodkow

        When wprowadzi Jerzy jako nazwe odbiorcy platnosci

        And wprowadzi 100 jako kwote

        And przesle zadanie Przelewu srodkow

        Then upewnij sie, ze przelew srodkow zakonczy sie pomyslnie komunikatem 100 PLN pomyslnie przelano do Jerzy!!

Scenario: Nieprawidlowy odbiorca platnosci

        Given uzytkownik jest na stronie Przelew srodkow

        When wprowadzi Unmesh jako nazwe odbiorcy platnosci

        And wprowadzi 100 jako kwote

        And przesle zadanie Przelewu srodkow

        Then upewnij sie, ze zostanie wyswietlony komunikat o bledzie transakcji Przelew nieudany!! 'Unmesh' nie jest zarejestrowany na twojej liscie odbiorcow

Scenario: Proba przelewu kwoty przekraczajacej dopuszczalny debet

        Given uzytkownik jest na stronie Przelew srodkow

        When wprowadzi Tomasz jako nazwe odbiorcy platnosci

        And wprowadzi 1000000 jako kwote

        And przesle zadanie Przelewu srodkow

        Then upewnij sie, ze zostanie wyswietlony komunikat o bledzie transakcji Przelew nieudany! Nie mozna przelac wiecej niz wynosi saldo konta

Następnie dla pliku opisu cechy funkcjonalnej utworzonego wcześniej opracuj plik definicji kroku. Ten plik mapuje każdy krok z pliku opisu cechy funkcjonalnej na metodę Pythona, która wywołuje API Selenium WebDriver. W folderze steps utwórz nowy plik skryptu Pythona fundtransfer_steps.py zawierający następujący kod.

from behave import given, when, then

@given (u"uzytkownik jest na stronie Przelew srodkow")

def step_user_is_on_fund_transfer_page(context):

    context.driver.get("http://localhost/fundtransfer/fundTransfer1.html")

@when(u"wprowadzi {name} jako nazwe odbiorcy platnosci")

def step_he_enters_payee_name(context, name):

    context.driver.find_element_by_id("payee").send_keys(name)

@when(u"wprowadzi {amount} jako kwote")

def step_he_enters_amount(context, amount):

    context.driver.find_element_by_id("amount").send_keys(amount)

@when(u"przesle zadanie Przelewu srodkow")

def step_he_enters_amount(context):

    context.driver.find_element_by_id("transfer").click()

@then(u"upewnij sie, ze przelew srodkow zakonczy sie pomyslnie komunikatem {text}")

def step_ensure_fund_transfer_is_complete(context, text):

    assert context.driver.find_element_by_id("message").text == text

@then(u"upewnij sie, ze zostanie wyswietlony komunikat o bledzie transakcji {text}")

def step_ensure_fund_transfer_is_complete(context, text):

    assert context.driver.find_element_by_id("message").text == text

Na koniec trzeba utworzyć plik konfiguracyjny, w którym są zamieszczone parametry wymagane do uruchomienia przez Behave cech funkcjonalnych zdefiniowanych w plikach opisu cech. W folderze fundtransfer utwórz plik environment.py zawierający następujący kod.

from selenium import webdriver

def before_all(context):

    context.driver = webdriver.Chrome()

def after_all(context):

    context.driver.quit()

Teraz należy skopiować wszystkie wymienione wcześniej pliki do katalogu o nazwie fundtransfer i użyć polecenia behave.

behave

Jak to działa?

Behave, podobnie jak Cucumber, wymaga napisania pliku opisu cech funkcjonalnych w języku Gherkin. Pliki opisu cech funkcjonalnych są powiązane z definicjami kroków zamieszczonymi w osobnych plikach definicji kroków.

Aby uruchomić te kroki z biblioteką Behave, potrzebny jest plik konfiguracyjny, który daje dostęp do egzemplarza sterownika Selenium WebDriver.

from selenium import webdriver

def before_all(context):

    context.driver = webdriver.Chrome()

def after_all(context):

    context.driver.quit()

Gdy Behave przetwarza pliki opisu funkcji, generowany jest domyślny raport w następującym formacie (zobacz zrzut poniżej).

Zobacz też

Receptura „Wykorzystanie technik BDD w Javie za pomocą biblioteki Cucumber-JVM i frameworka Selenium WebDriver”,

Receptura „Wykorzystanie technik BDD w środowisku .NET za pomocą biblioteki SpecFlow.NET i frameworka Selenium WebDriver”,

Receptura „Wykorzystanie biblioteki Capybara, Cucumber i frameworka Selenium WebDriver w Ruby”.

Rozdział 12.
Integracja z innymi narzędziami

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Konfigurowanie systemu ciągłej integracji Jenkins.

Wykorzystanie systemów Jenkins i Maven do egzekucji testów Selenium WebDriver w trybie ciągłej integracji.

Wykorzystanie systemu Ant do uruchamiania testów Selenium WebDriver.

Wykorzystanie systemów Jenkins i Ant do uruchamiania testów Selenium WebDriver w środowisku ciągłej integracji.

Automatyzacja aplikacji z niewebowymi interfejsami użytkownika z wykorzystaniem Selenium WebDriver oraz AutoIt.

Automatyzacja aplikacji z niewebowymi interfejsami użytkownika z wykorzystaniem Selenium WebDriver oraz Sikuli.

Wprowadzenie

Selenium WebDriver jest powszechnie stosowany w kombinacji z różnymi innymi narzędziami do tworzenia frameworków automatyzacji testów.

W początkowych recepturach tego rozdziału zaprezentowałem integrację frameworka Selenium WebDriver z różnymi narzędziami wytwarzania oprogramowania i budowania aplikacji, takimi jak Maven, Ant czy też serwer CI Jenkins. Wymienione narzędzia zapewniają prosty sposób rozwijania frameworków automatyzacji testów oraz rozszerzają możliwości API Selenium WebDriver w kierunku tworzenia rozwiązania ciągłego testowania. W recepturach zamieszczonych w tym rozdziale wyjaśniam, jak zainstalować i skonfigurować te narzędzia do pracy z Selenium.

Na koniec pokażę, w jaki sposób skorzystać z API Selenium WebDriver wraz z takimi narzędziami jak AutoIt i Sikuli w celu zautomatyzowania aplikacji z interfejsami niewebowymi. Oba narzędzia są znane w świecie oprogramowania darmowego oraz open source. Są wykorzystywane do automatyzacji zadań użytkownika i zapewniają oryginalne podejście do automatyzowania GUI.

Konfigurowanie systemu ciągłej integracji Jenkins

Jenkins jest popularnym serwerem ciągłej integracji używanym w społeczności programistów Javy. Wywodzi się z serwera CI Hudson. Obsługuje narzędzia SCM[1], takie jak CVS, Subversion, Git, Mercurial, Perforce i ClearCase. Umożliwia uruchamianie projektów bazujących na Apache Maven i Apache Ant, a także dowolnych skryptów powłoki oraz poleceń wsadowych w systemie Windows.

System Jenkins można zainstalować w celu skonfigurowania środowiska zautomatyzowanych testów, w którym testy Selenium WebDriver są uruchamiane w trybie nienadzorowanym na podstawie zdefiniowanego harmonogramu lub za każdym razem, gdy w systemach SCM zostaną zatwierdzone zmiany.

W tej recepturze skonfiguruję serwer Jenkins w celu uruchamiania projektów Ant i Maven. W kolejnych recepturach opiszę sposób wykorzystania narzędzi Ant i Maven do uruchamiania testów Selenium WebDriver razem z Jenkins.

Przygotuj się

Pobierz serwer CI Jenkins spod adresu http://jenkins-ci.org/ i zainstaluj go. W tej recepturze skorzystano z instalatora Jenkins dla Windows w celu zainstalowania systemu Jenkins na komputerze z systemem Windows 7.

Jak to zrobić?

Aby można było skorzystać z systemu Jenkins, należy ustawić w jego konfiguracji wymienione poniżej opcje.

Przejdź w oknie przeglądarki do pulpitu nawigacyjnego Jenkins (domyślnie localhost:8080).

Na pulpicie nawigacyjnym Jenkins kliknij łącze Zarządzaj Jenkinsem.

Na stronie Zarządzaj Jenkinsem kliknij łącze Skonfiguruj system.

Dodawanie JDK

Aby dodać JDK, wykonaj następujące czynności.

Na stronie Skonfiguruj system zlokalizuj sekcję JDK.

W sekcji JDK kliknij przycisk Dodaj.

W polu Nazwa wpisz JDK8 i anuluj zaznaczenie pola wyboru Install automatically.

W polu tekstowym JAVA_HOME wprowadź ścieżkę folderu JDK w Twoim systemie. Na poniższym zrzucie ekranu wprowadzono ścieżkę C:\Program Files\Java\jdk1.8.0_111.

Dodawanie Ant

Na stronie Skonfiguruj system zlokalizuj sekcję Ant.

W sekcji Ant kliknij przycisk Dodaj.

W polu Ant Name wpisz Ant i anuluj zaznaczenie pola wyboru Install automatically.

W polu tekstowym ANT_HOME wprowadź ścieżkę folderu Ant z systemu. Na poniższym zrzucie ekranu wprowadzono ścieżkę C:\Program Files(x86)\WinAnt, określającą lokalizację wersji WinAnt.

Dodawanie Maven

Aby dodać Maven, wykonaj następujące czynności.

Na stronie Skonfiguruj system zlokalizuj sekcję Maven.

W sekcji Maven kliknij przycisk Dodaj.

W polu Maven: Name wpisz Maven i anuluj zaznaczenie pola wyboru Install automatically.

W polu tekstowym MAVEN_HOME wprowadź ścieżkę folderu Maven z systemu. Na poniższym zrzucie ekranu zmienna MAVEN_HOME ma wartość C:\apache-maven-3.3.9.

Aby zapisać konfigurację, kliknij przycisk Save.

Co dalej?

W systemie Jenkins działa również autonomiczny serwer Selenium, który może spełniać funkcję zdalnego sterownika webowego. Korzystając z architektury master-slave systemu Jenkins, możemy tworzyć środowisko rozproszonego budowania i testowania przydatne do zastosowania w dużych projektach automatyzacji testów.

Zobacz też

Receptura „Wykorzystanie systemów Jenkins i Maven do egzekucji testów Selenium WebDriver w środowisku ciągłej integracji”,

Receptura „Wykorzystanie systemów Jenkins i Ant do uruchamiania testów Selenium WebDriver w środowisku ciągłej integracji”.

Wykorzystanie systemów Jenkins i Maven do egzekucji testów Selenium WebDriver w środowisku ciągłej integracji

System Jenkins pozwala na korzystanie z systemu Maven w celu tworzenia i testowania projektu w środowisku ciągłej integracji. W tej recepturze skonfiguruję system Jenkins do uruchamiania testów dla projektu Maven.

Przygotuj się

Uruchamianie testów z Jenkins i Maven wymaga zainstalowania na komputerze obu tych narzędzi. W tej recepturze wykorzystano projekt SeleniumCookbook z zamieszczonej w rozdziale 1. receptury „Konfigurowanie środowiska tworzenia testów Selenium WebDriver dla języka Java z Eclipse i Maven”.

W tej recepturze wykorzystano system Subversion w roli narzędzia zarządzania kodem źródłowym (SCM) projektu SeleniumCookbook.

Jenkins pozwala na używanie różnych narzędzi SCM. Jeśli aktualna instalacja Jenkins nie obsługuje Twojego narzędzia SCM, poszukaj odpowiedniej wtyczki w katalogu wtyczek systemu Jenkins.

Jak to zrobić?

Przejdź w oknie przeglądarki do panelu Jenkins (domyślnie localhost:8080).

Aby utworzyć nowe zadanie CI, kliknij link Nowy Projekt na pulpicie Jenkins Dashboard.

W polu tekstowym Nazwa projektu wprowadź Selenium Cookbook.

Zaznacz przełącznik Maven project, tak jak pokazano na poniższym zrzucie ekranu.

Kliknij OK.

Zostanie utworzone nowe zadanie o podanej nazwie.

Na stronie konfiguracji zadania przejdź do sekcji Repozytorium kodu źródłowego i wybierz przełącznik Subversion.

W polu tekstowym Repository URL wprowadź adres URL kodu testu, tak jak pokazano na poniższym zrzucie ekranu.

Opcjonalnie Jenkins poprosi o dane logowania do repozytorium systemu Subversion. Podaj poświadczenia użytkownika zgodnie z tym, jak zostały skonfigurowane na serwerze SVN.

Przejdź do sekcji Build. W polu tekstowym Root POM wprowadź pom.xml, a w polu tekstowym Goals and options wprowadź clean test, tak jak pokazano na poniższym zrzucie ekranu.

Na stronie projektu Selenium Cookbook kliknij link Build Now. Wróć do pulpitu Jenkins Dashboard.

Maven zbuduje projekt i uruchomi testy, które są w nim zdefiniowane. Po zakończeniu procesu budowania kliknij projekt Selenium Cookbook na liście, tak jak pokazano na poniższym zrzucie ekranu.

Na stronie projektu Selenium Cookbook wyświetli się historia budowania oraz łącza do wyników, tak jak pokazano na pierwszym zrzucie ekranu poniżej.

Aby przejrzeć wyniki testów, kliknij link Ostatni wynik testów, tak jak pokazano na drugim zrzucie ekranu poniżej.

Planowanie kompilacji w celu automatycznej egzekucji

W systemie Jenkins przejdź do konfiguracji projektu Selenium Cookbook.

W sekcji Build Triggers zaznacz pole wyboru Build periodically.

W polu tekstowym Schedule wprowadź 0 22 * * *, tak jak pokazano na zrzucie ekranu poniżej. Spowoduje to inicjowanie procesu budowania codziennie o 22.00 oraz uruchamianie testów w trybie nienadzorowanym.

Aby zapisać konfigurację, kliknij przycisk Save.

Jak to działa?

Opcja Maven Project umożliwia tworzenie i testowanie projektów Maven.

Jenkins obsługuje różne narzędzia SCM, takie jak CVS i Subversion. Aby uzyskać kod źródłowy z systemu SCM, wprowadź lokalizację repozytorium i określ strategię pobierania. Ponieważ w tym przykładzie używamy systemu Maven, należy określić ścieżkę głównego katalogu POM oraz cel Maven.

Podczas budowania projektu Jenkins pobiera z systemu SCM do obszaru roboczego projektu najnowsze źródła. Następnie wywołuje system Maven dla określonego celu. Po zakończeniu procesu budowania Jenkins pobiera wyniki testów z Maven i wyświetla wyniki na stronie projektu.

Planowanie kompilacji

Jedną z najważniejszych cech funkcjonalnych systemu Jenkins jest automatyczne wyzwalanie kompilacji na podstawie zdefiniowanych kryteriów. Za pośrednictwem konfiguracji Build Trigger Jenkins oferuje różne sposoby uruchamiania procesu budowania. Proces budowania może być wyzwalany o określonej godzinie. W poprzednim przykładzie zaplanowano budowanie projektu codziennie o godzinie 22.00. Taka konfiguracja zapewnia możliwość nocnego uruchamiania testów w trybie bez nadzoru. Dzięki temu można obejrzeć wyniki testów następnego dnia rano.

Wyniki testów

Jenkins oferuje możliwość wyświetlania wyników testu poprzez czytanie plików wyników wygenerowanych przez frameworki testów jednostkowych. Pozwala także na archiwizację tych wyników, dzięki czemu z biegiem czasu możliwe jest generowanie różnych metryk.

Zobacz też

Receptura „Konfigurowanie systemu ciągłej integracji Jenkins”.

Wykorzystanie systemu Ant do uruchamiania testów Selenium WebDriver

Apache Ant jest popularnym narzędziem zarządzania kompilacjami dostępnym dla programistów Javy. Jest podobny do Apache Maven, ale w odróżnieniu od Maven nie obsługuje zarządzania projektami ani zależnościami. Jest to narzędzie służące wyłącznie do budowania.

Ant zapewnia inny sposób uruchamiania testów Selenium WebDriver z wiersza polecenia lub za pomocą narzędzi ciągłej integracji, takich jak Jenkins.

W tej recepturze dodam obsługę Ant do projektu SeleniumCookbook utworzonego w recepturze „Konfigurowanie środowiska tworzenia testów Selenium WebDriver dla języka Java z Eclipse i Maven”.

Przygotuj się

W systemie Windows można pobrać i zainstalować program WinAnt. WinAnt jest dostarczany razem z programem instalacyjnym, który może służyć do skonfigurowania narzędzia. Instalator WinAnt jest dostępny pod adresem http://code.google.com/p/winant/.

Można również pobrać i zainstalować systemy Ant dla innych systemów operacyjnych. Są dostępne pod adresem http://ant.apache.org/bindownload.cgi. W tej recepturze wykorzystano narzędzie WinAnt w systemie operacyjnym Windows.

Jak to zrobić?

Aby skonfigurować projekt SeleniumCookbook do pracy z Ant, wykonaj następujące czynności.

Utwórz folder lib i skopiuj pliki JAR zależności używanych w projekcie — tzn. Selenium WebDriver i JUnit.

W folderze projektu utwórz plik build.xml o następującej zawartości.

<?xml version="1.0" encoding="UTF-8"?>

<project name="test" default="exec" basedir=".">

<property name="src" value="./src" />

<property name="lib" value="./lib" />

<property name="bin" value="./bin" />

<property name="report" value="./report" />

<path id="test.classpath">

    <pathelement location="${bin}" />

    <fileset dir="${lib}">

        <include name="**/*.jar" />

    </fileset>

</path>

<target name="init">

    <delete dir="${bin}" />

    <mkdir dir="${bin}" />

</target>

<target name="compile" depends="init">

    <javac source="1.6" srcdir="${src}" fork="true" destdir="${bin}" >

        <classpath>

                <pathelement path="${bin}">

                </pathelement>

            <fileset dir="${lib}">

                <include name="**/*.jar" />

            </fileset>

        </classpath>

    </javac>

</target>

<target name="exec" depends="compile">

    <delete dir="${report}" />

    <mkdir dir="${report}" />

        <mkdir dir="${report}/xml" />

    <junit printsummary="yes" haltonfailure="no">

        <classpath>

            <pathelement location="${bin}" />

            <fileset dir="${lib}">

                <include name="**/*.jar" />

            </fileset>

        </classpath>

        <test name="seleniumcookbook.

            examples.test.GoogleSearchTest"

            haltonfailure="no" todir="${report}/xml"

            outfile="TEST-result">

            <formatter type="xml" />

        </test>

    </junit>

    <junitreport todir="${report}">

        <fileset dir="${report}/xml">

            <include name="TEST*.xml" />

        </fileset>

        <report format="frames"

            todir="${report}/html" />

        </junitreport>

    </target>

</project>

W wierszu polecenia przejdź do katalogu projektu i wpisz następujące polecenie.

ant

Spowoduje to rozpoczęcie procesu kompilacji. Na ekranie będą wyświetlane wyniki uruchamianych testów. Na koniec Ant utworzy w folderze projektu folder report. Aby wyświetlić wyniki, przejdź w folderze report do podfolderu html i otwórz plik index.html.

Jak to działa?

Ant potrzebuje pliku build.xml zawierającego opcje konfiguracji oraz definicje kroków, które są potrzebne do zbudowania projektu. Do pliku build.xml można dodawać kroki generowania raportów, wysyłania powiadomień e-mail i tak dalej. Ant dostarcza dynamiczny framework definiowania kroków w procesie budowania.

Ant potrzebuje również niezbędnych plików bibliotek (JAR). Należy je skopiować do folderu lib.

Ant skanuje wszystkie testy w projekcie i uruchamia je w sposób bardzo podobny do sposobu ich uruchamiania w środowisku Maven.

Zobacz też

Receptura „Wykorzystanie systemów Jenkins i Ant do uruchamiania testów Selenium WebDriver w środowisku ciągłej integracji”.

Wykorzystanie systemów Jenkins i Ant do uruchamiania testów Selenium WebDriver w środowisku ciągłej integracji

Narzędzie Ant w połączeniu z systemem Jenkins można również skonﬁgurować do uruchamiania testów w środowisku ciągłej integracji. W tej recepturze skonfiguruję system Jenkins do uruchamiania testów razem z Ant.

Przygotuj się

Uruchamianie testów z Jenkins i Ant wymaga zainstalowania na komputerze obu tych narzędzi. Opis instalacji i konfiguracji narzędzi Ant i Jenkins można znaleźć w recepturach — odpowiednio — „Wykorzystanie systemu Ant do uruchamiania testów Selenium WebDriver” oraz „Konfigurowanie systemu ciągłej integracji Jenkins”.

Jak to zrobić?

Aby skonfigurować narzędzia Jenkins i Ant do działania w środowisku ciągłej integracji, wykonaj następujące czynności.

Przejdź w oknie przeglądarki do pulpitu nawigacyjnego Jenkins Dashboard (domyślnie localhost:8080).

Aby utworzyć nowe zadanie CI, kliknij link Nowy Projekt na pulpicie Jenkins Dashboard.

W polu Nazwa Projektu wpisz Selenium Cookbook.

Zaznacz przełącznik Freestyle project, tak jak pokazano na poniższym zrzucie ekranu.

Kliknij OK.

Zostanie utworzone nowe zadanie o nazwie Selenium Cookbook.

Na stronie konfiguracji zadania przejdź do sekcji Repozytorium kodu źródłowego i wybierz przełącznik Subversion.

W polu tekstowym Repository URL wprowadź adres URL kodu testu, tak jak pokazano na pierwszym zrzucie ekranu poniżej. Opcjonalnie Jenkins poprosi o dane logowania Subversion. Podaj poświadczenia użytkownika zgodnie z tym, jak zostały skonfigurowane na serwerze SVN.

Przejdź do sekcji Build. Kliknij przycisk Add build step ponownie i z rozwijanej listy wybierz opcję Invoke Ant.

W polu tekstowym Ant version wyświetla się opcja Default, tak jak pokazano na drugim zrzucie ekranu poniżej.

Przejdź do sekcji Post-build Actions.

Kliknij przycisk Add post-build action i z rozwijanej listy wybierz opcję Publish JUnit test result report.

W polu tekstowym Test report XMLs wprowadź **/report/*.xml, tak jak pokazano na poniższym zrzucie ekranu.

Aby zapisać konfigurację, kliknij przycisk Zapisz.

Wróć do strony pulpitu nawigacyjnego Jenkins Dashboard.

Aby zainicjować proces budowania, kliknij przycisk Schedule a Build. Ant uruchomi test. Po zakończeniu procesu budowania kliknij projekt Selenium Cookbook na liście, tak jak pokazano na poniższym zrzucie ekranu.

Na stronie projektu wyświetla się historia budowania oraz łącza do wyników, tak jak pokazano na pierwszym zrzucie ekranu poniżej.

Aby przejrzeć wyniki testów, kliknij link Ostatni wynik testów, tak jak pokazano na drugim zrzucie ekranu poniżej.

Planowanie kompilacji w celu automatycznego uruchamiania testów

W systemie Jenkins przejdź do konfiguracji projektu Selenium Cookbook.

W sekcji Build Triggers zaznacz pole wyboru Build periodically.

W polu tekstowym Schedule wprowadź 0 22 * * *, tak jak pokazano na zrzucie ekranu poniżej.

Spowoduje to inicjowanie procesu budowania codziennie o godz. 22.00.

Aby zapisać konfigurację, kliknij przycisk Zapisz.

Zobacz też

Receptura „Wykorzystanie systemu Ant do uruchamiania testów Selenium WebDriver”,

Receptura „Konfigurowanie systemu ciągłej integracji Jenkins”.

Automatyzacja aplikacji z niewebowymi interfejsami użytkownika z wykorzystaniem Selenium WebDriver oraz AutoIt

Selenium WebDriver to API automatyzacji przeglądarek działające z elementami HTML aplikacji webowych. Nie obsługuje aplikacji z natywnymi interfejsami użytkownika budowanymi w językach C++, .NET, Java lub z wykorzystaniem innych technologii komputerów desktop. Selenium WebDriver nie zawiera również wbudowanej obsługi kontrolek Flex, Flash lub Silverlight.

Podczas testowania aplikacji, które współdziałają z natywnymi interfejsami użytkownika, trudności sprawia automatyzacja ich funkcjonalności. Przykładowo aplikacja webowa udostępnia funkcję przesyłania plików, która wywołuje natywne okno dialogowe wyboru pliku.

Do obsługi natywnych interfejsów użytkownika można wykorzystać takie narzędzia jak AutoIt. Jest to darmowy język skryptowy podobny do języka BASIC, zaprojektowany pod kątem automatyzowania GUI Windows oraz do tworzenia skryptów ogólnego przeznaczenia. Korzystając z AutoIt, można wykonywać zadania automatyzacji — symulować kombinacje naciśnięć klawiszy, ruchu myszą i manipulowania oknami oraz kontrolkami. To bardzo niewielkie i samodzielne narzędzie. Działa we wszystkich wersjach systemu operacyjnego Windows. Skrypty AutoIt można skompilować do postaci samodzielnych plików wykonywalnych.

AutoIt ma pewne ograniczenia pod względem obsługiwanego systemu operacyjnego. Nie jest dostępny w systemach Linux i Mac OSX oraz nie działa ze sterownikiem RemoteWebDriver.

W tej recepturze omówię integrację narzędzia AutoIt z Selenium WebDriver w celu przetestowania funkcji przesyłania plików na przykładowej stronie.

Przygotuj się

Pobierz narzędzie AutoIt spod adresu http://www.autoitscript.com/site/autoit/downloads/ i zainstaluj je.

Jak to zrobić?

Do zaimplementowania funkcjonalności przesyłania plików na serwer można skorzystać z wielu bibliotek lub wtyczek, które dostarczają szereg dodatkowych funkcji. W tej recepturze skorzystamy z wtyczki jQuery File Upload. Oferuje ona użytkownikom wiele sposobów przesyłania plików na serwer. Mogą oni przeciągać i upuszczać pliki w przeglądarce lub kliknąć przycisk Add Files, aby otworzyć natywne okno dialogowe Otwieranie, podobne do pokazanego na poniższym zrzucie ekranu.

Zautomatyzujemy test, w którym użytkownik klika przycisk Add Files w celu przesłania pliku na serwer. Powoduje to wywołanie okna dialogowego Otwieranie, podobnego do tego, które pokazano na poprzednim zrzucie ekranu. W celu automatyzacji interakcji w tym oknie dialogowym utworzymy skrypt AutoIt.

Tworzenie skryptu AutoIt

Utwórzmy skrypt AutoIt, który działa z oknem dialogowym Otwieranie.

Uruchom Edytor skryptów SciTE poprzez wybranie polecenia Start/AutoIt.

Utwórz nowy skrypt poprzez wybranie polecenia File/New z głównego menu programu SciTE Script Editor.

Nadaj temu plikowi nazwę OpenDialogHandler.au3 i zapisz go (skrypty AutoIt mają rozszerzenie .au3). Skopiuj do skryptu poniższy kod, a następnie zapisz skrypt.

WinWaitActive("Otwieranie","","20")

If WinExists("Otwieranie") Then

    ControlSetText("Otwieranie","","Edit1",$CmdLine[1])

    ControlClick("Otwieranie","","&Otwórz")

EndIf

Uruchom narzędzie Compile Script to .exe z menu Start/AutoIt. Za pomocą narzędzia Compile Script to .exe można wykonać konwersję skryptu AutoIt na plik wykonywalny.

W oknie Aut2Exe v3 - AutoIt Script to EXE Converter w polu tekstowym Source (AutoIt .au3) wprowadź ścieżkę skryptu AutoIt, a w polu tekstowym Destination — ścieżkę do pliku wykonywalnego (plik .exe/.a3x). Następnie kliknij przycisk Convert, tak jak pokazano na poniższym zrzucie ekranu.

Wykorzystanie skryptu OpenDialogHandler w skrypcie Selenium WebDriver

Teraz utworzymy test, który pozwala kliknąć przycisk Add Files, po czym wywołuje program OpenDialogHandler.exe i weryfikuje, czy został przesłany właściwy plik.

Utwórz nową klasę testu o nazwie FileUpload i skopiuj do niej poniższy kod.

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.By;

import org.openqa.selenium.support.ui.ExpectedCondition;

import org.openqa.selenium.support.ui.WebDriverWait;

import org.junit.*;

import static org.junit.Assert.*;

public class FileUpload {

    protected WebDriver driver;

    @Before

    public void setUp() {

        driver = new ChromeDriver();

        driver.get("http://blueimp.github.com/jQuery-File-Upload/");

    }

    @Test

    public void testFileUpload() throws

        InterruptedException {

        try {

            // Kliknięcie przycisku Add Files

            driver.findElement(By.className("fileinput-button")).click();

            // Wywołanie skryptu OpenDialogHandler. Podanie

            // ścieżki do wgrywanego pliku

            Runtime.getRuntime().exec(new String[]

                {"C:\\chapter12\\OpenDialogHandler.exe","\"C:\\chapter12\\

Picture1.png\""});

            // Oczekiwanie na zakończenie operacji przesyłania pliku

            boolean result = (new WebDriverWait(driver, 30)).until(new ExpectedCondition<Boolean>() {

    public Boolean apply(WebDriver d) {

        return d.findElement(By.xpath("//table[@role='presentation']"))

       .findElements(By.tagName

        ("tr")).size() > 0;

    }});

    assertTrue(result);

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

    @After

    public void tearDown() {

        driver.close();

    }

}

Jak to działa?

AutoIt dostarcza interfejs API do automatyzacji natywnych kontrolek Windows UI. W tym przykładzie wykorzystaliśmy funkcję WinWaitActive(), która czeka na okno. Pobiera argumenty w postaci tytułu oczekiwanego okna, tekstu okna oraz limitu czasu. W przykładzie podaliśmy tytuł Otwórz oraz określiliśmy limit czasu 20 sekund. Argument reprezentujący tekst okna przekazaliśmy jako pusty, ponieważ do zidentyfikowania okna wystarczy tytuł.

WinWaitActive("Otwieranie","","20")

Po uaktywnieniu otwartego okna wywoływana jest funkcja ControlSetText() służąca do wprowadzania tekstu w polu Nazwa pliku.

ControlSetText("Otwieranie","","Edit1",$CmdLine[1])

Funkcja pobiera argumenty w postaci tytułu okna, tekstu okna, identyfikatora kontrolki oraz tekstu, który należy wpisać w polu tekstowym. Do identyfikowania kontrolek AutoIt używa identyfikatorów, które spełniają rolę podobną do lokalizatorów stosowanych we frameworku Selenium. Identyfikatory kontrolek można ustalić za pomocą narzędzia AutoIt V3 Window Info instalowanego razem z AutoIt. Przy użyciu tego narzędzia można „szpiegować” okna lub kontrolki i odczytywać różne właściwości. W tym przykładzie funkcja ControlSetText() przyjmuje ostatni parametr jako $CmdLine[1]. Zamiast kodowania „na sztywno” ścieżki do pliku, który chcemy wgrać na serwer, przekazujemy nazwę pliku do programu AutoIt za pomocą argumentów wiersza polecenia; ta wartość będzie zapisana w $CmdLine[1].

W celu kliknięcia przycisku Otwórz wywoływana jest funkcja ControlClick(). Funkcja ta pobiera parametry w postaci tytułu okna, tekstu w oknie oraz identyfikatora kontrolki.

ControlClick("Otwieranie","","&Otwórz")

Więcej informacji na temat API narzędzia AutoIt można znaleźć w dokumentacji pomocy programu AutoIt.

Za pomocą narzędzia Aut2Exe v3 - AutoIt Script to EXE Converter skrypt AutoIt jest kompilowany do postaci pliku wykonywalnego, tzn. OpenDialogHandler.exe.

Test Selenium WebDriver wywołuje ten plik wykonywalny z wykorzystaniem metody exec() klasy RunTime. Za jej pośrednictwem kod Javy może komunikować się z środowiskiem, w którym jest uruchomiony. Pełna ścieżka do programu OpenDialogHandler, a także ścieżka do pliku, który ma być przesłany, są przekazywane za pośrednictwem metody exec(). Należy pamiętać, że argumenty programu OpenDialogHandler należy ująć w cudzysłów.

Runtime.getRuntime().exec(new String[]

    {"C:\\chapter12\\OpenDialogHandler.exe",

    "\"C:\\chapter12\\Picture1.png\""});

Co dalej?

Skrypty AutoIt mogą być wykorzystywane za pośrednictwem innych dowiązań biblioteki Selenium WebDriver — na przykład .NET, Ruby i Python, jeśli tylko języki te pozwalają na wywoływanie zewnętrznych procesów.

Z narzędziem AutoIt jest również dostarczana prosta biblioteka COM AutoItX, którą można wykorzystać w językach obsługujących model COM (ang. Component Object Model). Skorzystanie z API modelu COM pozwala uniknąć konieczności pisania skryptów AutoIt i kompilowania ich do postaci pliku wykonywalnego.

W niektórych aplikacjach webowych wykorzystywany jest mechanizm uwierzytelniania HTTP, który przed wyświetleniem aplikacji wymaga od użytkowników uwierzytelniania. W takich aplikacjach wyświetlane jest okno dialogowe uwierzytelniania HTTP, podobne do tego, które pokazano na poniższym zrzucie ekranu.

To okno dialogowe jest wyświetlane za pomocą natywnej kontrolki interfejsu użytkownika. Poza tym układ i rodzaj kontrolek wyświetlanych w tym oknie dialogowym może się zmieniać dla różnych przeglądarek. W poniższym przykładzie bezpośrednio wywołano AutoItX API w skrypcie Ruby, aby zautomatyzować okno dialogowe uwierzytelniania HTTP wyświetlane w Google Chrome.

require 'rubygems'

require 'selenium-webdriver'

require 'test/unit'

require 'win32ole'

class HttpAuthTest < Test::Unit::TestCase

    def setup

        @driver = Selenium::WebDriver.for :chrome

        @driver.get 'http://www.httpwatch.com/httpgallery/authentication'

        @verification_errors = []

        end

        def test_http_auth_window

            # Utworzenie egzemplarza kontrolki AutoItX3.

            # Dzięki temu uzyskamy dostęp do API modelu COM AutoItX

            au3 = WIN32OLE.new("AutoItX3.Control")

            # Uzyskanie dostępu do przycisku Display Image

            # i kliknięcie go w celu wyświetlenia okna dialogowego uwierzytelniania HTTP

            display_image_button = @driver.find_element :id =>

                "displayImage"

            display_image_button.click

            # Oczekiwanie przez kilka sekund na wyświetlenie okna dialogowego

            # uwierzytelniania HTTP

            sleep(2)

            # Sprawdzenie, czy jest wyświetlane okno uwierzytelniania HTTP. Jeśli tak, to

            # wprowadzamy dane logowania za pomocą metody send

            result = au3.WinExists("HTTP Watch - Google Chrome")

            if result then

                au3.WinActivate("HTTP Watch - Google Chrome","")

                au3.Send("httpwatch{TAB}")

                au3.Send("jsdhfkhkhfd{Enter}")

            end

            assert_equal 1, result

        end

    def teardown

        @driver.quit

        assert_equal [], @verification_errors

    end

end

W poprzednim przykładzie do utworzenia egzemplarza interfejsu COM AutoItX użyto modułu Ruby win32ole.

au3 = WIN32OLE.new("AutoItX3.Control")

Za pomocą tego interfejsu można wywoływać metody AutoIt w celu interakcji z niewebowymi interfejsami użytkownika.

Zobacz też

Receptura „Automatyzacja aplikacji z niewebowymi interfejsami użytkownika z wykorzystaniem Selenium WebDriver oraz Sikuli”.

Automatyzacja aplikacji z niewebowymi interfejsami użytkownika z wykorzystaniem Selenium WebDriver oraz Sikuli

Sikuli to kolejne narzędzie, którego można użyć razem z Selenium WebDriver do automatyzacji niewebowych interfejsów użytkownika. Narzędzie to wykorzystuje technologię wizualnej identyfikacji do automatyzacji i testowania graficznych interfejsów użytkownika (GUI). Skrypt Sikuli nie automatyzuje wywołań API, ale wszystkie operacje, które użytkownik wykonuje na ekranie.

Narzędzie Sikuli jest obsługiwane w systemach operacyjnych Windows, Linux i Mac OSX. Sikuli, podobnie jak Selenium, dostarcza środowisko IDE do tworzenia skryptów oraz interfejs API, który może być używany w środowisku Javy.

Sikuli dobrze się sprawdza do automatyzacji aplikacji z niewebowymi interfejsami użytkownika. Ma jednak również pewne ograniczenia. Jednym z nich jest brak obsługi sterownika RemoteWebDriver. Skrypt Sikuli może zakończyć się niepowodzeniem, w przypadku gdy nie znajdzie przechwyconego obrazu z powodu nakładających się okien w czasie wykonywania aplikacji.

W tej recepturze omówię integrację API Sikuli z frameworkiem Selenium WebDriver w celu testowania niewebowych interfejsów użytkownika.

Przygotuj się

Pobierz narzędzie Sikuli spod adresu i http://sikuli.org/ i zainstaluj je.

Jak to zrobić?

Do automatyzacji kroków testu za pomocą Sikuli wykorzystamy przykład z uwierzytelnianiem HTTP z poprzedniej receptury.

Przed przystąpieniem do automatyzacji czynności w oknie dialogowym Wymagane uwierzytelnienie należy przechwycić obrazy pól tekstowych Nazwa użytkownika: i Hasło: oraz przycisku Zaloguj się (zobacz zrzut poniżej).

Wykonaj zrzut ekranu okna dialogowego Wymagane uwierzytelnienie i wyodrębnij z niego obrazy poszczególnych kontrolek, tak jak pokazano na rysunku poniżej.

Zapisz wyodrębnione obrazy osobno, w formacie PNG (ang. Portable Network Graphics), w folderze, do którego można uzyskać łatwy dostęp z testów.

Dodaj do projektu testowego plik Sikuli-script.jar. Plik można uzyskać z folderu instalacji programu Sikuli.

Utwórz nowy test, nadaj mu nazwę HttpAuthTest i skopiuj do niego poniższy kod.

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.chrome.ChromeDriver;

import org.openqa.selenium.By;

import org.sikuli.script.FindFailed;

import org.sikuli.script.Screen;

import org.junit.*;

import static org.junit.Assert.fail;

public class HttpAuthTest {

    private WebDriver driver;

    private StringBuffer verificationErrors = new

        StringBuffer();

    @Before

    public void setUp() {

        driver = new ChromeDriver();

        driver.get("http://www.httpwatch.com/httpgallery/authentication/");

    }

    @Test

    public void testHttpAuth() throws

        InterruptedException {

        driver.findElement(By.id

        ("displayImage")).click();

        // Pobranie systemowego ekranu

        Screen s = new Screen();

        try {

            // Komenda type systemu Sikuli skorzysta z pliku obrazu

            // kontrolki

            // oraz tekstu, który należy wprowadzić w

            // kontrolce

            s.type("C:\\UserName.png", "httpwatch");

            s.type("C:\\Password.png","dhjhfj");

            // Komenda click systemu Sikuli skorzysta z pliku obrazu

            // kontrolki

            s.click("C:\\Login.png");

    } catch (FindFailed e) {

        // Jeśli Sikuli nie znajdzie obrazu na ekranie,

// to zgłasza wyjątek FindFailed

e.printStackTrace();

}

    }

    @After

    public void tearDown() {

        driver.close();

        String verificationErrorString =

            verificationErrors.toString();

        if (!"".equals(verificationErrorString)) {

            fail(verificationErrorString);

        }

    }

}

Jak to działa?

Ponieważ do identyfikacji i interakcji z oknami i kontrolkami Sikuli używa technologii wizualnej identyfikacji, do wykonania swoich działań wymaga obrazów kontrolek. Podczas wykonywania testu lokalizuje regiony przechwycone z obrazu i wykonuje podane działania.

s.type("C:\\UserName.png", "httpwatch");

W tym przykładzie przeszukuje ekran w celu odnalezienia regionu, który pasuje do obrazu przechwyconego w pliku UserName.png, i wykonuje polecenie type(). Sikuli odtwarza te polecenia w taki sam sposób, jakby na ekranie pracował rzeczywisty użytkownik.

W przypadku gdy obszar przechwycony wewnątrz obrazu nie jest wyświetlany na ekranie albo jest przykryty przez inne okna, wtedy interakcje aplikacji z programem Sikuli mogą zakończyć się niepowodzeniem.

Co dalej?

Za pomocą programu Sikuli można zintegrować z frameworkiem Selenium WebDriver technologie RIA, takie jak Flash i Silverlight.

Zobacz też

Receptura „Automatyzacja aplikacji z niewebowymi interfejsami użytkownika z wykorzystaniem Selenium WebDriver oraz AutoIt”.

[1] Ang. Source Code Management — dosł. oprogramowanie do zarządzania kodem źródłowym — przyp. tłum.

Rozdział 13.
Testy dla wielu przeglądarek

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Konfigurowanie programu Selenium Grid Server do pracy w trybie równoległego uruchamiania testów.

Dodawanie węzłów do siatki Selenium Grid w celu testowania różnych przeglądarek.

Tworzenie i wykonywanie skryptu Selenium równolegle z TestNG.

Tworzenie i wykonywanie skryptu Selenium równolegle z Python.

Wykorzystanie narzędzi w chmurze do testowania różnych przeglądarek.

Uruchamianie testów w trybie headless z PhantomJS.

Wprowadzenie

Aby zmniejszyć koszty i zwiększyć wydajność, organizacje wdrażają technologie wirtualizacji oraz używają chmury. Wirtualizację i infrastrukturę chmurową można również wykorzystać do skalowania automatyzacji testów poprzez zmniejszenie inwestycji w fizyczny sprzęt potrzebny do skonfigurowania środowiska testowego. Z pomocą tych technologii aplikacje webowe można testować, stosując różne kombinacje przeglądarek i systemów operacyjnych. Selenium WebDriver zapewnia obsługę testowania aplikacji w środowisku wirtualnym i równoległego uruchamiania testów, co przyczynia się do obniżania kosztów oraz zwiększenia szybkości i zakresu działania testów. W tym rozdziale zamieszczono receptury służące do konfigurowania i używania testów Selenium WebDriver w trybie uruchamiania równoległego i rozproszonego.

Równoległe wykonywanie testów wymaga dwóch rzeczy, czyli infrastruktury do dystrybucji testów oraz frameworka, który pozwala uruchamiać te testy równolegle w określonej infrastrukturze. W tym rozdziale najpierw utworzę infrastrukturę rozproszoną, a następnie napiszę kilka testów, które będą uruchamiane w tym rozproszonym środowisku testowym.

Selenium Grid w przezroczysty sposób rozdziela testy na wiele maszyn fizycznych bądź wirtualnych. Wtedy testy można uruchamiać równolegle, co zmniejsza czas wymagany do ich uruchamiania. Taki zabieg znacznie przyspiesza testowanie oraz pozwala szybko uzyskać dokładne wyniki.

Selenium Grid pozwala na wykorzystanie istniejącej infrastruktury obliczeniowej. Umożliwia łatwe uruchamianie wielu testów równolegle, na wielu węzłach lub klientach, w środowisku heterogenicznym, w którym wykorzystywanych jest wiele systemów operacyjnych i przeglądarek. Oto przykład architektury Selenium Grid zapewniającej możliwość uruchamiania testów na platformach Linux, Windows, Mac, iOS i Android.

Selenium Grid pozwala na równoległe uruchamianie wielu egzemplarzy WebDriver lub Selenium Remote Control. Użycie tego środowiska sprawia, że wszystkie węzły są widziane jako pojedynczy egzemplarz. Dzięki temu nie trzeba przejmować się rzeczywistą infrastrukturą. Zastosowanie Selenium Grid redukuje czas uruchamiania testów do ułamka czasu, jaki zajęłoby uruchomienie pojedynczego egzemplarza Selenium. Dodatkowo środowisko to bardzo łatwo się konfiguruje i wykorzystuje.

Autonomiczny pakiet Selenium Server zawiera koncentrator (ang. hub), WebDriver i Selenium RC. Komponenty te są wymagane do uruchomienia środowiska Grid.

Testowanie frameworka równoległego uruchamiania

W tym rozdziale potrzebujemy frameworka, który obsługuje równoległe uruchamianie testów. Do równoległego uruchamiania testów z dowiązaniami Selenium WebDriver dla języka Java użyjemy narzędzia TestNG (http://testng.org/). W celu uruchomienia wielu egzemplarzy tego samego testu TestNG używa modelu wątków oraz konfiguracji napisanej w języku XML. Środowisko TestNG trochę przypomina JUnit.

Skorzystamy również z surowej metody równoległego uruchamiania testów dla dowiązań dla języka Python, za pośrednictwem modułu subprocess. Do równoległego uruchamiania testów w Pythonie można użyć również modułu nose.

Aby uruchomić testy równolegle z dowiązaniami .NET, można skorzystać z narzędzi PUnit lub MSTEST, natomiast dla Ruby funkcjonalność tę zapewnia gem DeepTest.

Konfigurowanie programu Selenium Grid Server do pracy w trybie równoległego uruchamiania testów

Aby można było uruchomić testy Selenium WebDriver równolegle, trzeba skonfigurować Selenium Grid Server jako koncentrator. Koncentrator Selenium Server dostarcza testom Selenium WebDriver dostępne konfiguracje lub zdolności. Z koncentratorem są połączone maszyny podrzędne (ang. slaves), co umożliwia równoległe uruchamianie testów. W testach Selenium WebDriver wykorzystywany jest protokół komunikacyjny JSON. Za jego pomocą możliwe jest przekazywanie poleceń Selenium do koncentratora.

Koncentrator spełnia rolę centralnego punktu, który odbiera żądanie całego testu i rozpowszechnia je do właściwych węzłów.

W tej recepturze skonfiguruję środowisko Selenium Grid Server, a następnie dodam węzły z różnymi kombinacjami systemów operacyjnych i przeglądarek. Następnie użyję tej konfiguracji w celu równoległego uruchomienia testów z wykorzystaniem narzędzia TestNG.

Przygotuj się

Spod adresu http://docs.seleniumhq.org/download/ pobierz plik JAR z najnowszą wersją Selenium Server. W tej recepturze wykorzystano wersję selenium-server-standalone-3.0.1.

Jak to zrobić?

Skonfigurowanie koncentratora jest dość prostym zadaniem. Uruchom Selenium Server za pomocą następującego polecenia.

java -jar selenium-server-standalone-3.0.1.jar -port 4444 -role hub

Powyższe polecenie uruchamia system Selenium Server w roli koncentratora. W wierszu polecenia wyświetla się następujący wynik.

Jak to działa?

Po uruchomieniu serwera Selenium Standalone Server w roli koncentratora rozpoczyna on słuchanie węzłów i testów Selenium w porcie 4444. Po przejściu pod adres http://localhost:4444/ grid/console na koncentratorze w przeglądarce wyświetlają się następujące informacje.

Co dalej?

Aby możliwe było uruchamianie testów z Selenium Grid, testy Selenium WebDriver wymagają użycia egzemplarzy klas RemoteWebDriver i DesiredCapabilities. Za ich pomocą definiujemy wykorzystywaną w testach przeglądarkę, wersję i platformę. Na podstawie preferencji ustawionych za pośrednictwem egzemplarza DesiredCapabilities koncentrator skieruje test do węzła odpowiadającego tym preferencjom. W tym przykładzie koncentrator skieruje test do węzła z systemem Windows oraz przeglądarką Firefox o podanej wersji.

DesiredCapabilities cap = new DesiredCapabilities();

    cap.setBrowserName("firefox");

    cap.setPlatform(org.openqa.selenium.Platform.WINDOWS);

Preferencje te są ustawiane za pomocą metod setBrowserName(), setVersion() i setPlatform() klasy DesiredCapabillities.

Egzemplarz klasy DesiredCapabilities jest przekazywany do egzemplarza RemoteWebDriver.

driver = new RemoteWebDriver(new

URL("http://192.168.1.100:4444/wd/hub"),cap);

W tym przykładzie test nawiązuje połączenie z koncentratorem uruchomionym pod adresem http://192.168.1.100:4444/wd/hub z wykorzystaniem egzemplarza klasy DesiredCapabilities o nazwie cap.

Dodawanie węzłów do siatki Selenium Grid w celu testowania różnych przeglądarek

W tej recepturze dodam węzły do koncentratora Selenium Hub skonfigurowanego w poprzedniej recepturze. W celu przetestowania różnych przeglądarek użyję wielu kombinacji platform systemu operacyjnego i przeglądarek.

Jak to zrobić?

Do koncentratora dodamy niżej opisane konfiguracje systemów operacyjnych i przeglądarek.

Dodawanie węzła przeglądarki IE

Zacznijmy od węzła, który zapewnia dostęp do przeglądarki Internet Explorer w systemie Windows. Otwórz nowe okno wiersza polecenia lub okno terminala i przejdź do lokalizacji, w której zapisano plik .jar serwera Selenium. Aby uruchomić i dodać węzeł do siatki Selenium Grid, trzeba wpisać następujące polecenie.

java -Dwebdriver.ie.driver="C:\SeDrivers\IEDriverServer.exe" -jar selenium-server-standalone-3.0.1.jar -role webdriver -browser "browserName=internet explorer,version=10,maxinstance=1, platform=WINDOWS" -hubHost 192.168.1.107 -port 5555

Aby dodać węzeł do siatki Selenium Grid, należy użyć argumentu -role i przekazać webdriver jako wartość. Trzeba również przekazać konfigurację przeglądarki dla węzła. Jest ona przekazywana za pośrednictwem argumentu browser. W tym przykładzie przekazaliśmy następujące argumenty: browserName jako internet explorer, version o wartości 10, maxinstance o wartości 1 oraz platform jako WINDOWS. Wartość maxinstance informuje siatkę Selenium Grid o liczbie równoległych egzemplarzy przeglądarek, jakie będą obsługiwane przez węzeł.

Aby nawiązać połączenie węzła z koncentratorem lub serwerem Selenium Grid, należy podać argument –hubHost i przekazać nazwę hosta lub adres IP serwera. Na koniec należy określić port, na którym będzie uruchomiony węzeł.

Po wydaniu poprzedniego polecenia i po uruchomieniu węzła w konsoli Grid wyświetli się następująca konfiguracja.

Alternatywnie można dodać węzeł, tworząc plik konfiguracji w formacie JSON, a następnie użyć następującego polecenia.

{

    "class": "org.openqa.grid.common.RegistrationRequest",

    "capabilities": [

        {

            "seleniumProtocol": "WebDriver",

            "browserName": "internet explorer",

            "version": "10",

            "maxInstances": 1,

            "platform" : "WINDOWS"

        }

    ],

    "configuration": {

    "port": 5555,

    "register": true,

    "host": "192.168.1.107",

    "proxy": "org.openqa.grid.selenium.proxy.

    DefaultRemoteProxy",

    "maxSession": 2,

    "hubHost": "192.168.1.100",

    "role": "webdriver",

    "registerCycle": 5000,

    "hub": "http://192.168.1.107:4444/grid/register",

    "hubPort": 4444,

    "remoteHost": "http://192.168.1.107:5555"

    }

}

Teraz możemy przekazać plik konfiguracyjny selenium-node-win-ie10.cfg.json za pomocą argumentów wiersza polecenia, tak jak pokazano w następującym poleceniu.

java -Dwebdriver.ie.driver="C:\SeDrivers\IEDriverServer.exe"-jar selenium-server-standalone-3.0.1.jar -role webdriver -nodeConfig selenium-node-win-ie10.cfg.json

Dodawanie węzła przeglądarki Firefox

Aby dodać węzeł przeglądarki Firefox, otwórz nowe okno wiersza polecenia lub terminalu i przejdź do lokalizacji, gdzie znajduje się plik .jar serwera Selenium. Aby uruchomić i dodać węzeł do siatki Selenium Grid, należy wpisać następujące polecenie.

java -jar selenium-server-standalone-3.0.1.jar -role webdriver -browser "browserName=firefox,version=38.0.5,maxinstance=2,platform=WINDOWS" -hubHost 192.168.1.107 –port 6666

W tym przykładzie ustawiliśmy maxinstance na 2. To informuje siatkę Selenium Grid, że ten węzeł będzie obsługiwał dwa egzemplarze programu Firefox. Po uruchomieniu węzła w konsoli Grid wyświetli się następująca konfiguracja.

Dodawanie węzła przeglądarki Chrome

Aby dodać węzeł przeglądarki Chrome, otwórz nowe okno wiersza polecenia lub okno terminalu i przejdź do lokalizacji, gdzie zapisano plik .jar serwera Selenium. Żeby uruchomić i dodać węzeł do siatki, należy wpisać następujące polecenie.

java -Dwebdriver.chrome.driver="C:\SeDrivers\chromedriver.exe" -jar selenium-server-standalone-3.0.1.jar -role webdriver -browser "browserName=chrome,version=35,maxinstance=2,platform=WINDOWS" -hubHost 192.168.1.107 -port 7777

Po uruchomieniu węzła w konsoli Grid wyświetli się następująca konfiguracja.

Mac OS X z Safari

Dodaliśmy egzemplarze reprezentujące przeglądarki IE, Firefox i Chrome na komputerze z systemem Windows. Spróbujmy teraz dodać węzeł Safari dla komputera z systemem Mac OS. Otwórz nowe okno terminala i przejdź do lokalizacji, w której zapisano plik .jar serwera Selenium. Aby uruchomić i dodać węzeł do siatki Selenium Grid, należy wpisać następujące polecenie.

java -jar selenium-server-standalone-3.0.1.jar -role webdriver -browser "browserName=safari,version=7,maxinstance=1,platform=MAC" -hubHost 192.168.1.107 -port 8888

Po uruchomieniu węzła w konsoli Grid wyświetli się następująca konfiguracja.

Tworzenie i równoległe wykonywanie skryptu Selenium z wykorzystaniem TestNG

Teraz, kiedy skonfigurowaliśmy koncentrator i węzły siatki Selenium Grid, nadszedł czas, by uruchomić testy równolegle w tym rozproszonym środowisku.

Aby uruchomić testy równolegle, potrzebujemy frameworka, który obsługuje dystrybucję testów na tych węzłach. Do tego celu użyjemy środowiska TestNG. Za jego pomocą uruchomimy równolegle testy napisane z wykorzystaniem dowiązań biblioteki Selenium WebDriver dla języka Java.

TestNG (http://testng.org/) to obok JUnit inny popularny framework testów jednostkowych używany przez członków społeczności Javy. TestNG pozwala tworzyć testy, które mogą być wykonywane równolegle. Możemy zestawić grupę testów lub zestawów testowych i skonfigurować parametry tak, aby uruchomić te testy równolegle. Do równoległego uruchamiania testów skorzystamy z modelu wielowątkowości.

TestNG wymaga pliku konfiguracyjnego XML zawierającego szczegóły konfiguracji, które są wymagane do równoległego uruchamiania testów. Przed uruchomieniem testu TestNG odczytuje ten plik konfiguracyjny w celu ustawienia parametrów.

W tej recepturze wykorzystam środowisko TestNG w celu utworzenia testów sparametryzowanych do współbieżnego działania. Receptura została podzielona na dwie części. W części pierwszej utworzę plik konfiguracyjny korzystający z koncentratora i węzłów, które zostały skonfigurowane dotychczas. Następnie, w części drugiej utworzę przypadek testowy z użyciem adnotacji TestNG oraz własności parametryzacji.

Przygotuj się

Upewnij się, że na komputerze, z którego będą uruchomione testy, zainstalowano środowisko TestNG. Potrzebne pliki można pobrać pod adresem http://testng.org/doc/index.html.

Jak to zrobić?

Aby utworzyć testy i uruchomić je równolegle, wykonaj następujące czynności.

Przed utworzeniem testu przeznaczonego do równoległego uruchomienia należy opracować plik konfiguracyjny wymagany przez narzędzie TestNG. Do testów przekażemy cztery parametry: platformę, przeglądarkę, wersję i URL.

Utwórz w katalogu źródłowym projektów nowy plik conf.xml i dodaj do niego następujący kod.

<!DOCTYPE suite SYSTEM http://testng.org/testng-1.0.dtd>

<suite name="Parallel Tests" verbose="1" thread-count="3"

parallel="tests">

</suite>

Dodaj do pliku conf.xml węzeł <test> reprezentujący test, który będzie wykonywany w systemie Windows z wykorzystaniem programu Internet Explorer.

<tests>

    <test name="Test Windows+IE10">

        <parameters>

            <parameter name="platform" value="Windows" />

            <parameter name="browser" value="Internet Explorer"/>

            <parameter name="version" value="10" />

            <parameter name="url" value="http://bit.ly/1zdNrFZ"/>

        </parameters>

        <classes>

            <class name="SeGridTest" />

        </classes>

    </test>

</tests>

Dodaj do pliku conf.xml kombinacje dla innych przeglądarek i systemów.

<suite name="Testy równoległe" verbose="1" thread-count="3" parallel="tests">

    <tests>

        <test name="Test Windows+IE10">

            <parameters>

                <parameter name="platform" value="Windows" />

                <parameter name="browser" value="Internet Explorer"/>

                <parameter name="version" value="10" />

                <parameter name="url" value="http://bit.ly/1zdNrFZ"/>

            </parameters>

            <classes>

                <class name="SeGridTest" />

            </classes>

        </test>

        <test name="Test Windows+Firefox">

            <parameters>

                <parameter name="platform" value="Windows" />

                <parameter name="browser" value="Firefox" />

                <parameter name="version" value="38.0.5" />

                <parameter name="url" value="http://bit.ly/1zdNrFZ" />

            </parameters>

            <classes>

                <class name="SeGridTest" />

            </classes>

        </test>

        <test name="Test Mac+Safari">

            <parameters>

                <parameter name="platform" value="Mac" />

                <parameter name="browser" value="Safari" />

                <parameter name="version" value="8.0.2" />

                <parameter name="url" value="http://bit.ly/1zdNrFZ" />

            </parameters>

            <classes>

                <class name="SeGridTest" />

            </classes>

        </test>

    </tests>

</suite>

Teraz utworzymy test przy użyciu frameworka TestNG. Opracujemy test z parametrami, korzystając z własności parametryzacji frameworka TestNG. Będziemy testować aplikację Kalkulator BMI. Będziemy używać standardowej wersji webowej oraz wersji mobilnej tej aplikacji. Utwórz nowy test o nazwie SeGridTest i skopiuj do niego poniższy kod.

package com.secookbook.examples.chapter13;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.remote.DesiredCapabilities;

import org.openqa.selenium.remote.RemoteWebDriver;

import static org.testng.Assert.*;

import org.testng.annotations.AfterTest;

import org.testng.annotations.Parameters;

import org.testng.annotations.Test;

import org.testng.annotations.BeforeTest;

import java.net.MalformedURLException;

import java.net.URL;

public class SeGridTest {

    WebDriver driver = null;

    @Parameters({ "platform", "browser", "version", "url" })

    @BeforeTest(alwaysRun = true)

    public void setup(String platform, String browser, String version,

        String url) throws MalformedURLException {

        DesiredCapabilities caps = new DesiredCapabilities();

        // Platformy

        if (platform.equalsIgnoreCase("Windows")) {

            caps.setPlatform(org.openqa.selenium.Platform.WINDOWS);

        }

        if (platform.equalsIgnoreCase("MAC")) {

            caps.setPlatform(org.openqa.selenium.Platform.MAC);

        }

        // Przeglądarki

        if (browser.equalsIgnoreCase("Internet Explorer")) {

            caps = DesiredCapabilities.internetExplorer();

        }

        if (browser.equalsIgnoreCase("Firefox")) {

            caps = DesiredCapabilities.firefox();

        }

        if (browser.equalsIgnoreCase("Chrome")) {

            caps = DesiredCapabilities.chrome();

        }

        if (browser.equalsIgnoreCase("Safari")) {

            caps = DesiredCapabilities.safari();

        }

        // Wersja

        caps.setVersion(version);

        driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"),

caps);

        // Otwarcie aplikacji Kalkulator BMI

        driver.get(url);

    }

    @Test(description = "Test Kalkulatora BMI")

    public void testBmiCalculator() throws

InterruptedException {

        WebElement height = driver.findElement(By.name("heightCMS"));

        height.sendKeys("181");

        WebElement weight = driver.findElement(By.name("weightKg"));

        weight.sendKeys("80");

        WebElement calculateButton = driver.findElement(By.id("Calculate"));

        calculateButton.click();

        WebElement bmi = driver.findElement(By.name("bmi"));

        assertEquals(bmi.getAttribute("value"), "24.4");

        WebElement bmiCategory = driver.findElement(By.name("bmi_category"));

        assertEquals(bmiCategory.getAttribute("value"), "Normal");

    }

    @AfterTest

    public void afterTest() {

        driver.quit();

    }

Jak to działa?

Najpierw utworzymy plik konfiguracji testu zawierający listę kombinacji testów, które chcemy uruchomić równolegle. Każdy egzemplarz testu jest zdefiniowany w tagu <test>.

<tests>

    <test name="Test Windows+IE10">

        <parameters>

            <parameter name="platform" value="Windows" />

            <parameter name="browser" value="Internet Explorer" />

            <parameter name="version" value="10" />

            <parameter name="url" value="http://bit.ly/1zdNrFZ" />

        </parameters>

        <classes>

            <class name="SeGridTest" />

        </classes>

    </test>

</tests>

W sekcji <parameters> określiliśmy platformę, przeglądarkę i wersję przeglądarki. Następnie dodaliśmy adres URL, ponieważ chcemy uruchomić ten test zarówno dla standardowej, jak i mobilnej wersji aplikacji webowej. Dalej, za pomocą węzła <class> określiliśmy nazwę klasy testu. Jeśli klasa testu jest przechowywana w pakiecie, należy określić pełną ścieżkę. Jeśli na przykład test SeGridTest jest zdefiniowany w pakiecie com.bmicalcapp.test, przekażemy następującą wartość.

<classes>

     <class name="com.bmicalcapp.test.SeGridTest" />

</classes>

Następnie, korzystając z frameworka TestNG, utworzyliśmy sparametryzowany test. Dodaliśmy metodę setup(), która przyjmuje argumenty do ustawiania opcji dla klasy DesiredCapabilities po to, by uruchomić testy dla każdej kombinacji określonej w pliku konfiguracyjnym.

@Parameters({ "platform", "browser", "version", "url" })

    @BeforeTest(alwaysRun = true)

    public void setup(String platform, String browser, String version, String url) throws MalformedURLException {

        DesiredCapabilities caps = new DesiredCapabilities();

        // Platformy

        if (platform.equalsIgnoreCase("Windows")) {

            caps.setPlatform(org.openqa.selenium.Platform.WINDOWS);

        }

        if (platform.equalsIgnoreCase("MAC")) {

            caps.setPlatform(org.openqa.selenium.Platform.MAC);

        }

        // Przeglądarki

        if (browser.equalsIgnoreCase("Internet Explorer")) {

            caps = DesiredCapabilities.internetExplorer();

        }

        if (browser.equalsIgnoreCase("Firefox")) {

            caps = DesiredCapabilities.firefox();

        }

        if (browser.equalsIgnoreCase("Chrome")) {

            caps = DesiredCapabilities.chrome();

        }

        if (browser.equalsIgnoreCase("Safari")) {

            caps = DesiredCapabilities.safari();

        }

        // Wersja

        caps.setVersion(version);

        driver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"),

caps);

        // Otwarcie aplikacji Kalkulator BMI

        driver.get(url);

}

Podczas uruchamiania testów należy zlecić frameworkowi TestNG czytanie konfiguracji testów z pliku conf.xml. TestNG odczyta ten plik i utworzy pulę wątków (w tym przykładzie będą to trzy wątki) dla każdej kombinacji wywołania klasy testowej. Następnie dla każdego wątku utworzy i przypisze egzemplarz klasy testu, przekazując parametry przeczytane z pliku konfiguracyjnego do metody setup(). TestNG uruchomi testy równolegle dla każdej odczytanej kombinacji. Jednocześnie będzie monitorował status testów oraz końcowe wyniki. Poniżej zaprezentowano raport wygenerowany po uruchomieniu testu dla kombinacji określonych w pliku konfiguracyjnym.

Tworzenie i równoległe wykonywanie skryptu Selenium z wykorzystaniem języka Python

W tej recepturze utworzę i uruchomię testy równolegle, korzystając z dowiązań biblioteki Selenium WebDriver dla Pythona oraz modułu subprocess. Do uruchomienia testów skorzystam z koncentratora i węzłów skonﬁgurowanych we wcześniejszych recepturach.

Jak to zrobić?

Utworzymy dwa skrypty testowe w celu przetestowania aplikacji w przeglądarkach Firefox i Internet Explorer. Aby to zrobić, wykonaj czynności opisane poniżej. Możesz również utworzyć pojedynczy test i sparametryzować go w sposób podobny do tego, z jakiego skorzystaliśmy, parametryzując test dla frameworka TestNG.

Pierwszy test w Pythonie wykorzystamy do przetestowania funkcjonalności aplikacji za pomocą przeglądarki Firefox. Nadaj temu testowi nazwę test_on_firefox.py i skopiuj do niego poniższy kod.

import unittest

from selenium import webdriver

from selenium.webdriver.common.desired_capabilities import

DesiredCapabilities

from selenium.webdriver.support.ui import WebDriverWait

class OnFirefox(unittest.TestCase):

    def setUp(self):

        self.driver = webdriver.Remote(

            command_executor='http://localhost:4444/wd/hub', desired_capabilities=DesiredCapabilities.FIREFOX)

    def test_gogle_search_ie(self):

        driver = self.driver

        driver.get("http://www.google.com")

        inputElement = driver.find_element_by_name("q")

        inputElement.send_keys("Ser!")

        inputElement.submit()

        WebDriverWait(driver, 20).until(lambda driver: driver.title.lower().startswith("ser!"))

        self.assertEqual("Ser! - Szukaj w Google",driver.title)

    def tearDown(self):

        self.driver.quit()

if __name__ == "__main__":

    unittest.main(verbosity=2)

Drugiego testu w Pythonie użyjemy do przetestowania funkcjonalności aplikacji z wykorzystaniem przeglądarki Internet Explorer. Nadaj temu testowi nazwę test_on_ie.py i skopiuj do niego poniższy kod.

import unittest

from selenium import webdriver

from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

from selenium.webdriver.support.ui import WebDriverWait

class OnFirefox(unittest.TestCase):

    def setUp(self):

        self.driver = webdriver.Remote(command_executor='http://localhost:4444/wd/hub',

            desired_capabilities=DesiredCapabilities.INTERNETEXPLORER)

    def test_google_search_ff(self):

        driver = self.driver

        driver.get("http://www.google.com")

        inputElement = driver.find_element_by_name("q")

        inputElement.send_keys("Ser!")

        inputElement.submit()

        WebDriverWait(driver, 20).until(lambda driver:

        driver.title.lower().startswith("ser!"))

        self.assertEqual("ser! - Szukaj w Google",

        driver.title)

    def tearDown(self):

        self.driver.quit()

if __name__ == "__main__":

    unittest.main(verbosity=2)

Na koniec utworzymy skrypt w Pythonie, w którym skorzystamy z modułu subprocess w celu równoległego uruchomienia tych testów na różnych węzłach. Nadaj temu skryptowi nazwę runner.py i skopiuj do niego poniższy kod.

import glob

from subprocess import Popen

tests = glob.glob('test*.py')

processes = [Popen('python %s' % test, shell=True) for test

in tests]

for process in processes:

    process.wait()

Jak to działa?

Wszystkie trzy skrypty zostały umieszczone w tym samym katalogu. Gdy uruchomimy skrypt runner.py, za pomocą funkcji glob zostaną wyszukane wszystkie testy o nazwach rozpoczynających się od słowa test. Następnie dołączyliśmy każdy z testów za pomocą funkcji Popen z modułu subprocess. Funkcja Popen() wywołuje każdy test w Pythonie jako podproces procesu głównego i czeka na zakończenie skryptu.

Co dalej?

Do równoległego uruchamiania testów w Pythonie możemy również użyć modułu nose. Najpierw należy zainstalować framework testów jednostkowych nose, korzystając z następującego polecenia.

pip nose

Po zainstalowaniu modułu nose należy otworzyć folder, w którym są przechowywane wszystkie testy, i skorzystać z następującego polecenia.

nosetests --processes=2

Spowoduje to wywołanie skryptów nosetests, które zlokalizują w bieżącym katalogu wszystkie pliki z prefiksem test i zainicjują rozpoczęcie równoległego uruchamiania testów. W tym przykładzie uruchamiamy dwa testy, więc musimy określić wartość dla argumentu processes jako 2. Moduł nose w celu obsługi dla współbieżnego uruchamiania wewnętrznie używa modułu multiprocessing.

Zobacz też

Receptura „Tworzenie i równoległe wykonywanie skryptu Selenium z wykorzystaniem TestNG”.

Wykorzystanie narzędzi w chmurze do testowania różnych przeglądarek i uruchamiania testów w chmurze

W poprzednich recepturach skonfigurowaliśmy lokalne środowisko Selenium Grid do uruchamiania testów w różnych przeglądarkach. Wymaga to skonfigurowania maszyn fizycznych lub wirtualnych z różnymi przeglądarkami i systemami operacyjnymi. Uruchomienie testów w laboratorium wymaga kosztów i wysiłku związanego z zebraniem potrzebnego sprzętu, oprogramowania i narzędzi pomocniczych. Należy także zadbać o to, aby ta infrastruktura była aktualizowana do najnowszych wersji, łatek i tak dalej. Nie każdy może sobie pozwolić na takie koszty i wysiłek.

Zamiast inwestowania i konfigurowania laboratorium do testowania wielu przeglądarek zadanie zorganizowania wirtualnego laboratorium testowego z łatwością można powierzyć zewnętrznemu dostawcy w chmurze. Sauce Labs i BrowserStack to wiodący dostawcy środowisk testowania wielu przeglądarek w chmurze. Obie te firmy zapewniają obsługę ponad 400 różnych konfiguracji przeglądarek i systemów operacyjnych, w tym urządzeń mobilnych i tabletów. W środowisku chmury udostępnianym przez te firmy można również uruchamiać testy Selenium WebDriver.

W tej recepturze pokażę, jak skonfigurować i uruchomić test w chmurze Sauce Labs. Uruchomienie testów w chmurze BrowserStack wymaga wykonania podobnych czynności.

Przygotuj się

Spróbujmy skonfigurować i uruchomić test w chmurze Sauce Labs. Na początek potrzebujemy darmowego konta Sauce Labs. Aby uzyskać za darmo nazwę użytkownika i klucz dostępu, należy zarejestrować konto w Sauce Labs, pod adresem https://saucelabs.com/. Sauce Labs udostępnia cały potrzebny sprzęt i infrastrukturę oprogramowania do uruchamiania testów w chmurze.

Klucz dostępu można uzyskać po zalogowaniu się do serwisu z własnego pulpitu (ang. dashboard).

Jak to zrobić?

Spróbujmy zmodyfikować test, który utworzyliśmy wcześniej do uruchamiania w środowisku Grid, aby uruchomić go w chmurze Sauce Labs. W teście należy dodać nazwę użytkownika serwisu Sauce Labs i klucz dostępu oraz zastąpić adres serwera Grid adresem środowiska Grid udostępnianego przez Sauce Labs. Można to zrobić, używając następującego kodu.

package com.secookbook.examples.chapter13;

import static org.junit.Assert.*;

import static org.testng.Assert.assertEquals;

import java.net.URL;

import java.text.MessageFormat;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.remote.DesiredCapabilities;

import org.openqa.selenium.remote.RemoteWebDriver;

public class SauceTest {

    WebDriver driver;

    @Before

    public void setUp() throws Exception {

        String SAUCE_USER = "<twoja_nazwa_użytkownika>";

        String SAUCE_KEY = "<twój_klucz_dostępu>";

        DesiredCapabilities caps = new DesiredCapabilities();

        caps.setCapability("platform", "OS X 10.9");

        caps.setCapability("browserName", "Safari");

        caps.setCapability("name", "Test Kalkulatora BMI");

        driver = new RemoteWebDriver(new

URL(MessageFormat.format("http://{0}:{1}@ondemand.saucelabs.com:80

/wd/hub'", SAUCE_USER, SAUCE_KEY)), caps);

        driver.get("http://bit.ly/1zdNrFZ");

    }

    @After

    public void tearDown() throws Exception {

        driver.quit();

    }

    @Test

    public void testBmiCalc() {

        WebElement height = driver.findElement(By.name("heightCMS"));

        height.sendKeys("181");

        WebElement weight = driver.findElement(By.name("weightKg"));

        weight.sendKeys("80");

        WebElement calculateButton = driver.findElement(By.id("Calculate"));

        calculateButton.click();

        WebElement bmi = driver.findElement(By.name("bmi"));

        assertEquals(bmi.getAttribute("value"), "24.4");

        WebElement bmiCategory =

    driver.findElement(By.name("bmi_category"));

        assertEquals(bmiCategory.getAttribute("value"), "Waga normalna");

    }

}

Listę platform obsługiwanych w serwisie Sauce Labs można uzyskać pod adresem https://saucelabs.com/platforms.

Podczas wykonywania testu zostanie nawiązane połączenie z koncentratorem Sauce Labs i wysłane żądanie określonej konfiguracji systemu operacyjnego i przeglądarki. Sauce Labs przypisuje maszynę wirtualną do uruchomienia testu w ustalonej konfiguracji. Podczas działania test możemy monitorować na pulpicie dashboard serwisu Sauce Labs, tak jak pokazano na poniższym zrzucie ekranu.

Możemy także przeglądać szczegóły sesji z serwisem Sauce, aby zobaczyć, co dokładnie wydarzyło się podczas testu. Dostępne są szczegóły poleceń Selenium, zrzuty ekranu, logi i zarejestrowane wideo z uruchomienia. Szczegóły te są widoczne za pośrednictwem wielu zakładek, tak jak pokazano na zrzucie ekranu poniżej.

Posługując się narzędziem Sauce Connect, można również testować aplikacje bezpiecznie zainstalowane na wewnętrznych serwerach. Sauce Connect tworzy bezpieczny tunel pomiędzy Twoim komputerem a chmurą Sauce.

Uruchamianie testów w trybie headless z wykorzystaniem PhantomJS

PhantomJS to bazująca na Webkit przeglądarka działająca w trybie headless. Przeglądarki PhantomJS możemy użyć w połączeniu z Selenium WebDriver do uruchomienia podstawowych testów funkcjonalnych.

W tej recepturze skonfiguruję i wykorzystam PhantomJS do uruchomienia prostego testu.

Przygotuj się

Do uruchomienia tego przykładu potrzebne będą binaria przeglądarki PhantomJS. Można je pobrać pod adresem http://phantomjs.org/.

W tym przykładzie używana jest wersja PhantomJS przeznaczona dla systemu Windows.

Trzeba również dodać zależność w pliku pom.xml narzędzia Maven w następujący sposób.

<dependency>

    <groupId>com.github.detro</groupId>

    <artifactId>phantomjsdriver</artifactId>

    <version>1.2.0</version>

    <scope>test</scope>

</dependency>

Jak to zrobić?

Utwórzmy nowy test PhantomjsTest, który używa PhantomJS do uruchomienia testu w trybie headless, tak jak pokazano w poniższym przykładzie kodu.

package com.secookbook.examples.chapter13;

import static org.junit.Assert.*;

import static org.testng.Assert.assertEquals;

import org.junit.After;

import org.junit.Before;

import org.junit.Test;

import org.openqa.selenium.By;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.phantomjs.PhantomJSDriver;

import org.openqa.selenium.phantomjs.PhantomJSDriverService;

import org.openqa.selenium.remote.DesiredCapabilities;

public class PhantomjsTest {

    WebDriver driver;

    @Before

    public void setUp() throws Exception {

        DesiredCapabilities caps = DesiredCapabilities.phantomjs();

        caps.setJavascriptEnabled(true);

        caps.setCapability(PhantomJSDriverService.PHANTOMJS_EXECUTABLE_PATH_PROPERTY,

"./src/test/resources/drivers/phantomjs.exe");

        driver = new PhantomJSDriver(caps);

        driver.get("http://bit.ly/1zdNrFZ");

    }

    @After

    public void tearDown() throws Exception {

        driver.quit();

    }

    @Test

    public void testBmiCalc() {

        WebElement height = driver.findElement(By.name("heightCMS"));

        height.sendKeys("181");

        WebElement weight = driver.findElement(By.name("weightKg"));

        weight.sendKeys("80");

        WebElement calculateButton = driver.findElement(By.id("Calculate"));

        calculateButton.click();

        WebElement bmi = driver.findElement(By.name("bmi"));

        assertEquals(bmi.getAttribute("value"), "24.4");

        WebElement bmiCategory = driver.findElement(By.name("bmiCategory"));

        assertEquals(bmi_category.getAttribute("value"),"Waga normalna");

    }

}

Jak to działa?

Gdy uruchamiamy testy z PhantomJS, nie otwiera się graficzne okno przeglądarki, w którym można obserwować wykonywane działania. Zamiast tego na konsoli wyświetla się dziennik wykonania podobny do tego, który pokazano na poniższym zrzucie ekranu.

Korzystanie z PhantomJS wymaga nowego egzemplarza klasy PhantomJSDriver utworzonego w sposób pokazany w poniższym fragmencie kodu.

driver = new PhantomJSDriver(caps);

Ścieżkę do programu wykonywalnego PhantomJS należy przekazać z wykorzystaniem obiektu klasy DesiredCapabilities w następujący sposób.

DesiredCapabilities caps = DesiredCapabilities.phantomjs();

caps.setJavascriptEnabled(true);

caps.setCapability(

PhantomJSDriverService.

PHANTOMJS_EXECUTABLE_PATH_PROPERTY,

"./src/test/resources/drivers/phantomjs.exe");

Co dalej?

Przeglądarkę PhantomJS można również uruchomić jako węzeł Selenium i podłączyć do koncentratora za pomocą jej binariów przy użyciu następującego wiersza polecenia.

phantomjs --webdriver=9090 --webdriver-selenium-gridhub=http://localhost:4444

Spowoduje to zarejestrowanie nowego węzła w siatce Selenium Grid. Przy użyciu RemoteWebDriver można uruchomić testy z wykorzystaniem następującego obiektu DesiredCapabilities.

DesiredCapabilities caps = DesiredCapabilities.phantomjs();

caps.setJavascriptEnabled(true);

driver = new RemoteWebDriver(new

URL("http://localhost:4444/wd/hub"), caps);

Rozdział 14.
Testowanie aplikacji w przeglądarkach mobilnych

Oto zagadnienia, jakie zostaną omówione w tym rozdziale.

Konfigurowanie Appium w celu testowania aplikacji mobilnych.

Testowanie mobilnych aplikacji webowych w systemie iOS z wykorzystaniem Appium.

Testowanie mobilnych aplikacji webowych w systemie Android z wykorzystaniem Appium.

Wprowadzenie

Rosnąca liczba użytkowników mobilnych spowodowała, że znacznie zwiększył się stopień wykorzystania smartfonów i tabletów. Aplikacje mobilne opanowały rynek konsumencki i korporacyjny. Komputery stacjonarne i laptopy zostały zastąpione urządzeniami mobilnymi. Małe i duże firmy mają olbrzymi potencjał wykorzystywania platform mobilnych jako kanału łączności z użytkownikami. Testowanie aplikacji mobilnych nabrało kluczowego znaczenia dla różnych platform mobilnych dostępnych na rynku. W tym rozdziale dowiesz się więcej o sposobach testowania aplikacji mobilnych z wykorzystaniem frameworka Selenium WebDriver oraz Appium.

Appium to framework automatyzacji urządzeń mobilnych o statusie open source, używany do testowania aplikacji mobilnych na platformach iOS i Android z wykorzystaniem protokołu komunikacyjnego JSON oraz frameworka Selenium WebDriver. Appium zastępuje dostępne w Selenium 2 interfejsy API iPhoneDriver i AndroidDriver, które były używane do testowania mobilnych aplikacji webowych.

Appium pozwala na wykorzystywanie i rozszerzanie istniejącego frameworka Selenium WebDriver do budowania testów dla aplikacji mobilnych. Ponieważ Appium do zarządzania testami korzysta z biblioteki WebDriver, zatem do pisania testów możemy użyć dowolnego języka, dla którego istnieje kliencka biblioteka Selenium. Appium obsługuje testowanie natywnych, hybrydowych i mobilnych aplikacji webowych. W tym rozdziale pokażę, w jaki sposób wykorzystać Appium do testowania mobilnych aplikacji webowych.

Będziemy testować mobilne aplikacje webowe utworzone z wykorzystaniem biblioteki jQuery Mobile dla systemów Android i iOS.

Konfigurowanie Appium do testowania aplikacji mobilnych

Przed rozpoczęciem testowania aplikacji mobilnych z wykorzystaniem Appium należy pobrać i zainstalować to narzędzie. Będziemy używać wersji Appium z interfejsem GUI. Aby uruchomić testy dla systemu iOS na urządzeniach iPhone lub iPad, należy zainstalować i skonfigurować Appium na komputerze z systemem Mac OS X. By przetestować aplikacje Android, można skonfigurować środowisko testowe na komputerze z systemem Windows lub Linux. Z pomocą nowej aplikacji Appium App dla Mac OS X konfiguracja Appium jest dość łatwa.

Oprócz wersji GUI można również zainstalować wersję Appium server, która działa z wykorzystaniem biblioteki Node.js.

Jak to zrobić?

Najnowszą wersję binariów Appium można pobrać pod adresem http://appium.io/. Na głównej stronie należy kliknąć przycisk Download Appium. Spowoduje to pobranie wersji zgodnej z wybranym systemem operacyjnym (zobacz zrzut poniżej).

W poniższych przykładach wykorzystamy Appium dla systemu Mac OS X.

Appium można zainstalować na komputerach Mac przez uruchomienie instalatora i skopiowanie Appium do folderu aplikacji.

Po uruchomieniu Appium z aplikacji po raz pierwszy wyświetli się pytanie o zezwolenie na uruchomienie symulatorów iOS.

Domyślnie Appium uruchamia się pod adresem http://127.0.0.1:4723 lub localhost. Jest to adres URL, pod który testy powinny kierować polecenia. W tej recepturze będziemy testować mobilną wersję przykładowej aplikacji, która została wykorzystana w książce poświęconej przeglądarce Safari na iPhone.

W głównym oknie Appium kliknij ikonę Apple. Otworzy się okno ustawień systemu iOS podobne do pokazanego na poniższym zrzucie ekranu.

W oknie dialogowym iOS Settings zaznacz pole wyboru Force Device i wprowadź w sekcji iOS wartość iPhone 4s. Zaznacz również pole wyboru Use Mobile Safari, tak jak pokazano na poniższym zrzucie ekranu.

Aby uruchomić serwer Appium, w oknie Appium kliknij przycisk Launch.

Testowanie mobilnych aplikacji webowych w systemie iOS z wykorzystaniem Appium

Appium zarządza automatyzacją poprzez użycie natywnego frameworka automatyzacji i dostarcza interfejs API bazujący na protokole komunikacyjnym WebDriver JSON. W celu automatyzacji aplikacji systemu iOS wykorzystuje funkcję UI Automation oferowaną jako część zbioru narzędzi Apple Instruments.

Appium działa jako serwer HTTP. Odbiera polecenia ze skryptów testu za pośrednictwem protokołu komunikacyjnego JSON. Appium wysyła te polecenia do narzędzia Apple Instruments, dzięki czemu mogą być uruchomione na symulatorze lub urządzeniu rzeczywistym. W ramach tego procesu Appium tłumaczy polecenia JSON na zrozumiałe przez Apple Instruments polecenia JavaScript automatyzacji interfejsu użytkownika. Apple Instruments dba o uruchomienie i zamknięcie aplikacji w symulatorze lub na urządzeniu. Proces ten pokazano na poniższym diagramie.

Podczas wykonywania polecenia aplikacji na symulatorze lub urządzeniu aplikacja docelowa wysyła odpowiedzi do Apple Instruments, a narzędzie to przesyła je z powrotem do Appium w formacie odpowiedzi JavaScript. Appium tłumaczy odpowiedzi JavaScript UI Automation na odpowiedzi protokołu komunikacyjnego JSON Selenium WebDriver, po czym wysyła je z powrotem do skryptu testowego.

Przygotuj się

Przed rozpoczęciem pisania testów z Appium trzeba dodać do projektu bibliotekę kliencką Appium. W tym przykładzie do zarządzania zależnościami użyto narzędzia Maven. W związku z tym, do pliku POM.xml dodamy następujące zależności biblioteki Appium Java Client.

<dependency>

    <groupId>IO.appium</groupId>

    <artifactId>Java klienta</artifactId>

    <version>3.1.0</version>

    <scope>test</scope>

</dependency>

Jak to zrobić?

Appium już działa, zatem utwórzmy test, który testuje aplikację Kalkulator BMI w przeglądarce Safari w urządzeniu iPhone. Utwórz nowy test MobileBmiCalculator zawierający następujący kod.

package com.secookbook.examples.chapter14.ios;

import java.net.URL;

import io.appium.java_client.ios.IOSDriver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.remote.DesiredCapabilities;

import org.junit.*;

import static org.junit.Assert.*;

public class MobileBmiCalculatorTest {

    private WebDriver driver;

    @Before

    public void setUp() throws Exception {

        // Ustawienie żądanych możliwości dla iOS i iPhone 6

        DesiredCapabilities caps = new DesiredCapabilities();

        caps.setCapability("platformName", "iOS");

        caps.setCapability("platformVersion", "8.4");

        caps.setCapability("deviceName", "iPhone 6");

        caps.setCapability("browserName", "safari");

        // Utworzenie egzemplarza IOSDriver do testowania platformy iOS.

        // Nawiązanie połączenia z lokalnym serwerem Appium uruchomionym na innym komputerze.

        // Do testowania aplikacji webowej użyjemy typu WebElement

        driver = new IOSDriver<WebElement>(new URL(

            "http://192.168.0.101:4723/wd/hub"), caps);

        // Otwarcie aplikacji mobilnej Kalkulator BMI

        driver.get("http://cookbook.seleniumacademy.com/mobilebmicalculator.html");

    }

    @Test

    public void testMobileBmiCalculator() throws Exception {

        // Pobranie pola wzrostu i ustawienie jego wartości

        WebElement height = driver.findElement(By.name("heightCMS"));

        height.sendKeys("181");

        // Pobranie pola wagi i ustawienie jego wartości

        WebElement weight = driver.findElement(By.name("weightKg"));

        weight.sendKeys("80");

        // Kliknięcie przycisku Oblicz

        WebElement calculateButton = driver.findElement(By.id("Calculate"));

        calculateButton.click();

        // Sprawdzenie współczynnika BMI

        WebElement bmi = driver.findElement(By.name("bmi"));

        assertEquals("24.4", bmi.getAttribute("value"));

        // Sprawdzenie kategorii BMI

        WebElement bmi_category = driver.findElement(By.name("bmi_category"));

        assertEquals("Normal", bmi_category.getAttribute("value"));

    }

    @After

    public void tearDown() throws Exception {

        // Zamknięcie przeglądarki

        driver.quit();

    }

}

Jak to działa?

Biblioteka Appium Java Client dostarcza klasę IOSDriver, która umożliwia uruchamianie testów dla platformy iOS za pośrednictwem Appium. Aby jednak w Appium można było skorzystać z pożądanej platformy, musimy przekazać zbiór koniecznych opcji (ang. capabilities), tak jak pokazano w poniższym kodzie.

DesiredCapabilities caps = new DesiredCapabilities();

caps.setCapability("platformName", "iOS");

caps.setCapability("platformVersion", "8.4");

caps.setCapability("deviceName", "iPhone 6");

caps.setCapability("browserName", "safari");

Opcja platformName jest używana przez Appium do podjęcia decyzji dotyczącej platformy, na której ma być uruchomiony skrypt testowy. W tym przykładzie użyliśmy platformy iPhone 6 Simulator. Aby uruchomić testy na iPadzie, możemy użyć symulatora iPad Simulator.

W przypadku uruchamiania testów na rzeczywistym urządzeniu należy dla opcji platformName określić iPhone lub iPad. Appium wybierze urządzenie, które jest podłączone do komputera Mac za pomocą kabla USB.

Ostatnią opcją, którą zastosowaliśmy, jest browserName. Jest ona używana przez Appium do uruchomienia przeglądarki Safari.

Na koniec należy nawiązać połączenie z serwerem Appium za pomocą IOSDriver i zaprogramować pożądane opcje. Można to zrobić przez utworzenie egzemplarza IOSDriver, tak jak pokazano w poniższym kodzie.

driver = new IOSDriver<WebElement>(new URL(

"http://192.168.0.101:4723/wd/hub"), caps);

W pozostałej części testu wykorzystano interfejs Selenium API do interakcji z mobilną wersją aplikacji webowej. Test można uruchomić w zwykły sposób. Po uruchomieniu testu widać, jak Appium ustanawia sesję ze skryptem testowym, a następnie uruchamia narzędzie iPhone Simulator z aplikacją Safari. Appium wykona wszystkie kroki testu poprzez uruchomienie poleceń aplikacji Safari w oknie symulatora.

Testowanie mobilnych aplikacji webowych w systemie Android z wykorzystaniem Appium

Appium zarządza automatyzacją aplikacji na Android przy użyciu interfejsu UI Automator dostarczanego razem z pakietem Andorid SDK. Proces jest podobny do testowania w systemie iOS.

Appium działa jako serwer HTTP. Odbiera polecenia ze skryptów testu za pośrednictwem protokołu komunikacyjnego JSON. Appium wysyła te komendy do narzędzia UI Automator, bo wtedy mogą być wykonywane w aplikacji uruchomionej na emulatorze lub rzeczywistym urządzeniu. Podczas tego procesu Appium tłumaczy polecenia JSON na zrozumiałe dla Android SDK polecenia interfejsu UI Automator. Proces ten pokazano na poniższym diagramie.

W przypadku uruchomienia polecenia aplikacji w emulatorze lub na urządzeniu aplikacja docelowa wysyła odpowiedź do interfejsu UI Automator. Następnie odpowiedź ta jest wysyłana z powrotem do Appium. Odpowiedzi interfejsu UI Automator są tłumaczone na odpowiedzi protokołu komunikacyjnego JSON Selenium WebDriver i przesyłane z powrotem do skryptu testowego.

Przygotuj się

Testowanie aplikacji w systemie Android jest podobne do testowania w iOS. Dla Androida użyjemy rzeczywistego urządzenia zamiast emulatora (w społeczności użytkowników Androida używa się określenia „emulator” w odniesieniu do symulatora). Do testowania w przeglądarce Chrome na Androidzie użyjemy tej samej aplikacji, co w poprzedniej recepturze.

W tym przykładzie skorzystano z urządzenia Samsung Galaxy S4 z Androidem. Na urządzeniu trzeba zainstalować przeglądarkę Google Chrome. Jeśli przeglądarka Google Chrome nie jest zainstalowana na urządzeniu, można ją pobrać ze sklepu Google Play. Następnie trzeba podłączyć urządzenie do komputera, na którym jest uruchomiony serwer Appium.

Aby uzyskać listę emulatorów lub urządzeń podłączonych do komputera, można skorzystać z następującego polecenia.

./adb devices

Android Debug Bridge (ADB) to działające w wierszu polecenia narzędzie dostępne w Android SDK, które pozwala komunikować się z egzemplarzem emulatora lub rzeczywistym urządzeniem Android podłączonym do komputera.

Polecenie ./adb devices wyświetla listę wszystkich urządzeń z systemem Android, które są podłączone do hosta. W tym przykładzie skorzystałem z połączenia z rzeczywistym urządzeniem, które wyświetliło się w sposób pokazany na poniższym zrzucie ekranu.

Jak to zrobić?

Zmodyfikujmy test utworzony dla iOS, żeby działał na Androidzie. Aby to zrobić, należy zmienić odpowiednie ustawienia, tak jak pokazano w poniższym przykładzie kodu.

package com.secookbook.examples.chapter14.android;

import java.net.URL;

import io.appium.java_client.android.AndroidDriver;

import org.openqa.selenium.WebDriver;

import org.openqa.selenium.WebElement;

import org.openqa.selenium.By;

import org.openqa.selenium.remote.DesiredCapabilities;

import org.junit.*;

import static org.junit.Assert.*;

public class MobileBmiCalculatorTest {

private WebDriver driver;

@Before

public void setUp() throws Exception {

    // Ustawienie żądanych opcji dla urządzenia z systemem Android

    DesiredCapabilities caps = DesiredCapabilities.android();

    caps.setCapability("deviceOrientation", "portrait");

    caps.setCapability("platformVersion", "4.4");

    caps.setCapability("platformName", "Android");

    caps.setCapability("browserName", "Chrome");

    // Utworzenie egzemplarza AndroidDriver do testowania na platformie Android.

    // Nawiązanie połączenia z lokalnym serwerem Appium uruchomionym na innym komputerze.

    // Do testowania aplikacji webowej użyjemy typu WebElement

    driver = new AndroidDriver<WebElement>new URL("http://192.168.0.101:4723/wd/hub"), caps);

    // Otwarcie aplikacji mobilnej Kalkulator BMI

    driver.get("http://localhost/mobilebmicalculator/mobilebmicalculator.html");

}

@Test

public void testMobileBmiCalculator() throws Exception {

    // Pobranie pola wzrostu i ustawienie jego wartości

    WebElement height = driver.findElement(By.name("heightCMS"));

    height.sendKeys("181");

    // Pobranie pola wagi i ustawienie jego wartości

    WebElement weight = driver.findElement(By.name("weightKg"));

    weight.sendKeys("80");

    // Kliknięcie przycisku Oblicz

    WebElement calculateButton = driver.findElement(By.id("Calculate"));

    calculateButton.click();

    // Sprawdzenie współczynnika BMI

    WebElement bmi = driver.findElement(By.name("bmi"));

    assertEquals("24.4", bmi.getAttribute("value"));

    // Sprawdzenie kategorii BMI

    WebElement bmi_category =

    driver.findElement(By.name("bmi_category"));

    assertEquals("Normal", bmi_category.getAttribute("value"));

}

@After

public void tearDown() throws Exception {

    // Zamknięcie przeglądarki

    driver.quit();

    }

}

Jak to działa?

W powyższym przykładzie przypisaliśmy do opcji platformName wartość Android. Opcję tę Appium wykorzysta w celu uruchomienia testów na Androidzie.

Ponieważ chcemy uruchomić testy w przeglądarce Chrome na Androidzie, w sekcji kodu opisującej przeglądarkę podaliśmy wartość Chrome. Inną ważną zmianą, jaką wykonaliśmy, jest zastosowanie klasy AndroidDriver z biblioteki klienckiej Appium Java.

Appium użyje pierwszego urządzenia z listy urządzeń zwróconych przez adb, tak jak pokazano na poniższym zrzucie ekranu. Używając wymienionych powyżej ustawień, uruchomi przeglądarkę Chrome na urządzeniu, a następnie rozpocznie wykonywanie poleceń skryptu testowego, tak jak pokazano na poniższym zrzucie ekranu.

Poniższy zrzut ekranu pokazuje test uruchomiony na rzeczywistym urządzeniu.